/ story-21-Composing-Algorithm-Operations.org
story-21-Composing-Algorithm-Operations.org
1 #+TITLE: Story 21 - Composing Algorithm Operations 2 #+AUTHOR: Le’ 3 #+OPTIONS: date:nil 4 #+LANGUAGE: en 5 #+CATEGORY: lesec 6 7 So far, algorithm operations are set up by name, and the responsibility 8 to provide composite algorithms (AES-CBC, HMAC-SHA256, or more complex 9 compositions like AES-GCM-SHA256) lies entirely on the plugin, thereby 10 presenting them as individual implementations. 11 12 This is, of course, perfectly legitimate, and even sensible from the point of 13 view that each algorithm composition is essentially a new algorithm. This is 14 also encouraged by the way algorithms are specified with OIDs in the protocols 15 that are governed by ASN.1 structures, as well as TLS. 16 17 However, this doesn't lend itself very well for plugins that only supplies 18 simple algorithms and expect them to be combined with others, or for the user 19 to experiment with new combinations that plugin authors haven't implemented. 20 This also opens the possibility to combine implementations from different 21 plugins without the plugin having to be aware of that[fn:: Plugins can still 22 refuse to work with other plugins, if they prefer. This is feasible for a 23 FIPS plugin, since they must be entirely self contained] 24 25 Is this possible to remedy? It should be, right? 26 27 I've been thinking for a while that this should be possible to specify with a 28 minilanguage of some sort. 29 30 This idea is "higher level" and therefore belongs in Le'Sec, not in Le'Sec 31 Core. 32 33 * A proposed syntax 34 35 The composite algorithm specification should be a simple string, and must be 36 possible to pass around as such. This syntax uses delimiters that are typical 37 for function calls, thereby implying that each algorithm is a function that 38 uses other components, such as other algorithms. 39 40 Example strings: 41 42 - ~hmac(hash=sha1)~ or possibly ~hmac(sha1)~ 43 - ~cbc(cipher=aes,iv=0x123456789ABCDEF)~ or possibly ~cbc(aes,iv=0x123456789ABCDEF)~ 44 - ~gcm(cipher=cbc(cipher=aes),hash=sha256)~ or possibly ~gcm(cbc(aes),sha256)~ 45 - ~gcm(cipher=aes-cbc,hash=sha256)~ or possibly ~gcm(aes-cbc,sha256)~ 46 47 Syntax specification in [[https://datatracker.ietf.org/doc/html/rfc5234][IETF ABNF]] form: 48 49 #+begin_src abnf 50 ; |name| is undefined here, pending a more proper definition in another story. 51 ; Essentially, it's assumed to be a sequence of character that won't confuse this 52 ; grammar. It's further assumed that if the name contains any of the characters 53 ; "<", ">", "[", "]", "{", "}", "(", ")", they are balanced. 54 55 algo = name 56 / ( name "(" args ")" ) 57 58 args = keyarg *( "," keyarg ) ; one or more keyargs 59 / arg *( "," arg ) *( "," keyarg ) ; one or more args + keyargs 60 arg = algo ; positional argument (see below) 61 keyarg = ( name "=" value ) ; keyword argument (see below) 62 63 value = algo 64 / number 65 / string 66 67 number = 1*DIGIT ; must not start with zero 68 / ( "0b" 1*BIT ) ; binary number 69 / ( "0o" 1*DIGIT ) ; only octal digits allowed 70 / ( "0x" 1*HEXDIG ) 71 string = %x22 *( WSP / VCHAR ) %x22 ; double quoted string 72 / %x27 *( WSP / VCHAR ) %x27 ; single quoted string 73 #+end_src 74 75 * Implementation 76 77 Le'Sec already has functionality to make operator objects, for example: 78 79 #+begin_src C 80 LESEC_EXPORT LE_STATUS 81 LeSec_make_encryptor(const char *id, 82 LeSec_find_encryptor_implementation_filter_fn *fn, 83 void *user_arg, 84 LeSec_encryptor_t **kp, 85 LeSec_env_t *env); 86 #+end_src 87 88 Functions like that can be refactored to parse the the specification above and 89 compose the final algorithm on behalf of the caller. 90 91 ** How to handle arguments 92 93 In this composite algorithm syntax specification, the "arguments" must be 94 passed to the algorithm they are given for. One way to do this is to pass 95 them as ~LSC_param_t~ parameters. 96 97 Keyword arguments should look up the keyword in a settable parameter description 98 to find out how to specify the parameter. 99 100 Positional arguments should look up the parameter index that corresponds to 101 its position to find out how to specify the parameter, where the first 102 position gets parameter index 1, the second position 2, etc. 103 104 The parameter descriptor for each argument value must be compatible with that 105 argument value. If there are ambiguities, the parameter descriptor governs 106 the acceptable argument value, not the other way around, making it possible to 107 have an algorithm named ~0xdeadbeef~. 108 109 Names are a bit special, as they could be passed as a string parameter 110 (~LSC_DT_utf8_string~), or as an operator object (~LSC_DT_object~). Again, 111 this is governed by the parameter descriptor, and then leaves it to the plugin 112 to decide what to do with them. 113 114 * What about keys? 115 116 In the discussion above on specifying composite algorithms, not one word has 117 been said about keys. After all, it wouldn't be /entirely/ strange to specify 118 something like this: 119 120 ~cbc(aes(key=0x000102030405060708090a0b0c0d0e0f),iv=0x123456789ABCDEF)~ 121 122 However, this isn't quite that easy to reconcile with current structures, and 123 becomes even more confusing with something like this: 124 125 ~cmac(aes(key=0x000102030405060708090a0b0c0d0e0f))~ 126 127 ~cmac(aes,key=0x000102030405060708090a0b0c0d0e0f)~ 128 129 Is the key an AES key or a CMAC-AES key? This is a point of view that could 130 affect how it's passed down to the plugin, and how it's handled by the plugin, 131 and may make it too easy for the user to do bad things (shoot themselves in 132 the foot). 133 134 Because of this uncertainty, I'm making a choice not to involve the key in the 135 composite algorithm specification and leave it to be specified separately. 136 The user will have to interact with the cretaed operation object to find out 137 what sort of key it must create, leaving the rest to the plugin. 138 139 This emphasise: *each implementation to which it's feasible to give a key must 140 be able to take a key, even if the implementation just passes it along to 141 another implementation.* 142 143 Each implementation will therefore also have to specify the correct key id. 144 If the key is governed by another implementation, it is that implementation's 145 key id that must be passed back to the caller.