-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy path03_Developing_Cipher_Algorithms.txt
165 lines (111 loc) · 8.86 KB
/
03_Developing_Cipher_Algorithms.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
== Developing Cipher Algorithms
=== Registering And Unregistering Transformation
There are three distinct types of registration functions in the Crypto API. One is used to register a generic cryptographic transformation, while the other two are specific to HASH transformations and COMPRESSion. We will discuss the latter two in a separate chapter, here we will only look at the generic ones.
Before discussing the register functions, the data structure to be filled with each, struct crypto_alg, must be considered -- see below for a description of this data structure.
The generic registration functions can be found in include/linux/crypto.h and their definition can be seen below. The former function registers a single transformation, while the latter works on an array of transformation descriptions. The latter is useful when registering transformations in bulk.
[source,c]
-------------
int crypto_register_alg(struct crypto_alg *alg);
int crypto_register_algs(struct crypto_alg *algs, int count);
-------------
The counterparts to those functions are listed below.
[source,c]
-------------
int crypto_unregister_alg(struct crypto_alg *alg);
int crypto_unregister_algs(struct crypto_alg *algs, int count);
-------------
Notice that both registration and unregistration functions do return a value, so make sure to handle errors. A return code of zero implies success. Any return code < 0 implies an error.
The bulk registration / unregistration functions require that struct crypto_alg is an array of count size. These functions simply loop over that array and register / unregister each individual algorithm. If an error occurs, the loop is terminated at the offending algorithm definition. That means, the algorithms prior to the offending algorithm are successfully registered. Note, the caller has no way of knowing which cipher implementations have successfully registered. If this is important to know, the caller should loop through the different implementations using the single instance *_alg functions for each individual implementation.
=== Single-Block Symmetric Ciphers [CIPHER]
Example of transformations: aes, arc4, ...
This section describes the simplest of all transformation implementations, that being the CIPHER type used for symmetric ciphers. The CIPHER type is used for transformations which operate on exactly one block at a time and there are no dependencies between blocks at all.
==== Registration specifics
The registration of [CIPHER] algorithm is specific in that struct crypto_alg field .cra_type is empty. The .cra_u.cipher has to be filled in with proper callbacks to implement this transformation.
See struct cipher_alg below.
==== Cipher Definition With struct cipher_alg
Struct cipher_alg defines a single block cipher.
Here are schematics of how these functions are called when operated from other part of the kernel. Note that the .cia_setkey() call might happen before or after any of these schematics happen, but must not happen during any of these are in-flight.
-----------------
KEY ---. PLAINTEXT ---.
v v
.cia_setkey() -> .cia_encrypt()
|
'-----> CIPHERTEXT
-----------------
Please note that a pattern where .cia_setkey() is called multiple times is also valid:
-----------------
KEY1 --. PLAINTEXT1 --. KEY2 --. PLAINTEXT2 --.
v v v v
.cia_setkey() -> .cia_encrypt() -> .cia_setkey() -> .cia_encrypt()
| |
'---> CIPHERTEXT1 '---> CIPHERTEXT2
-----------------
=== Multi-Block Ciphers [BLKCIPHER] [ABLKCIPHER]
Example of transformations: cbc(aes), ecb(arc4), ...
This section describes the multi-block cipher transformation implementations for both synchronous [BLKCIPHER] and asynchronous [ABLKCIPHER] case. The multi-block ciphers are used for transformations which operate on scatterlists of data supplied to the transformation functions. They output the result into a scatterlist of data as well.
==== Registration Specifics
The registration of [BLKCIPHER] or [ABLKCIPHER] algorithms is one of the most standard procedures throughout the crypto API.
Note, if a cipher implementation requires a proper alignment of data, the caller should use the functions of crypto_blkcipher_alignmask() or crypto_ablkcipher_alignmask() respectively to identify a memory alignment mask. The kernel crypto API is able to process requests that are unaligned. This implies, however, additional overhead as the kernel crypto API needs to perform the realignment of the data which may imply moving of data.
==== Cipher Definition With struct blkcipher_alg and ablkcipher_alg
Struct blkcipher_alg defines a synchronous block cipher whereas struct ablkcipher_alg defines an asynchronous block cipher.
Please refer to the single block cipher description for schematics of the block cipher usage. The usage patterns are exactly the same for [ABLKCIPHER] and [BLKCIPHER] as they are for plain [CIPHER].
==== Specifics Of Asynchronous Multi-Block Cipher
There are a couple of specifics to the [ABLKCIPHER] interface.
First of all, some of the drivers will want to use the Generic ScatterWalk in case the hardware needs to be fed separate chunks of the scatterlist which contains the plaintext and will contain the ciphertext. Please refer to the ScatterWalk interface offered by the Linux kernel scatter / gather list implementation.
=== Hashing [HASH]
Example of transformations: crc32, md5, sha1, sha256,...
==== Registering And Unregistering The Transformation
There are multiple ways to register a HASH transformation, depending on whether the transformation is synchronous [SHASH] or asynchronous [AHASH] and the amount of HASH transformations we are registering. You can find the prototypes defined in include/crypto/internal/hash.h:
[source,c]
-----------------
int crypto_register_ahash(struct ahash_alg *alg);
int crypto_register_shash(struct shash_alg *alg);
int crypto_register_shashes(struct shash_alg *algs, int count);
-----------------
The respective counterparts for unregistering the HASH transformation are as follows:
[source,c]
-----------------
int crypto_unregister_ahash(struct ahash_alg *alg);
int crypto_unregister_shash(struct shash_alg *alg);
int crypto_unregister_shashes(struct shash_alg *algs, int count);
-----------------
==== Cipher Definition With struct shash_alg and ahash_alg
Here are schematics of how these functions are called when operated from other part of the kernel. Note that the .setkey() call might happen before or after any of these schematics happen, but must not happen during any of these are in-flight. Please note that calling .init() followed immediately by .finish() is also a perfectly valid transformation.
----------------
I) DATA -----------.
v
.init() -> .update() -> .final() ! .update() might not be called
^ | | at all in this scenario.
'----' '---> HASH
II) DATA -----------.-----------.
v v
.init() -> .update() -> .finup() ! .update() may not be called
^ | | at all in this scenario.
'----' '---> HASH
III) DATA -----------.
v
.digest() ! The entire process is handled
| by the .digest() call.
'---------------> HASH
----------------
Here is a schematic of how the .export()/.import() functions are called when used from another part of the kernel.
----------------
KEY--. DATA--.
v v ! .update() may not be called
.setkey() -> .init() -> .update() -> .export() at all in this scenario.
^ | |
'-----' '--> PARTIAL_HASH
----------- other transformations happen here -----------
PARTIAL_HASH--. DATA1--.
v v
.import -> .update() -> .final() ! .update() may not be called
^ | | at all in this scenario.
'----' '--> HASH1
PARTIAL_HASH--. DATA2-.
v v
.import -> .finup()
|
'---------------> HASH2
----------------
==== Specifics Of Asynchronous HASH Transformation
Some of the drivers will want to use the Generic ScatterWalk in case the implementation needs to be fed separate chunks of the scatterlist which contains the input data. The buffer containing the resulting hash will always be properly aligned to .cra_alignmask so there is no need to worry about this.