mirror of https://gitee.com/openkylin/linux.git
256 lines
9.5 KiB
ReStructuredText
256 lines
9.5 KiB
ReStructuredText
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, for example when a driver
|
|
implements multiple transformations.
|
|
|
|
::
|
|
|
|
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.
|
|
|
|
::
|
|
|
|
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 register/unregister each
|
|
transformation in the given array of length count. They handle errors as
|
|
follows:
|
|
|
|
- crypto_register_algs() succeeds if and only if it successfully
|
|
registers all the given transformations. If an error occurs partway
|
|
through, then it rolls back successful registrations before returning
|
|
the error code. Note that if a driver needs to handle registration
|
|
errors for individual transformations, then it will need to use the
|
|
non-bulk function crypto_register_alg() instead.
|
|
|
|
- crypto_unregister_algs() tries to unregister all the given
|
|
transformations, continuing on error. It logs errors and always
|
|
returns zero.
|
|
|
|
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
|
|
-------------------
|
|
|
|
Example of transformations: cbc(aes), ecb(arc4), ...
|
|
|
|
This section describes the multi-block cipher transformation
|
|
implementations. 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 multi-block cipher 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_skcipher_alignmask() 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.
|
|
|
|
Specifics Of Asynchronous Multi-Block Cipher
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
There are a couple of specifics to the asynchronous 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:
|
|
|
|
::
|
|
|
|
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:
|
|
|
|
::
|
|
|
|
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
|
|
|
|
Note that it is perfectly legal to "abandon" a request object:
|
|
- call .init() and then (as many times) .update()
|
|
- _not_ call any of .final(), .finup() or .export() at any point in future
|
|
|
|
In other words implementations should mind the resource allocation and clean-up.
|
|
No resources related to request objects should remain allocated after a call
|
|
to .init() or .update(), since there might be no chance to free them.
|
|
|
|
|
|
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.
|