Linux Kernel for ARMにおける各種crypt実装が呼び出されるまで

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm/crypto

こちらをベースに、各種crypt実装が呼び出されるまでを簡単にまとめておく。

# 大前提

ARM archで、SHA1のNEON実装が有効になり、呼び出せる準備ができるまでをまとめる。

なお、 ** NEON/SIMDの実装の中身の議論はしない** です。　


# KConfig

arch/arm/crypy/Kconfig に、各種カーネルコンフィグレーションがある。

- ARMとなっているものは、基本コンパイルできるはず…だけどNEONも使っているものもまざってて、うんわかりにくいな。
- NEONとついているものは、NEONが有効になっている場合にだけ使える。
- CEとついているものは、ARM v8 Crypto Extensionsを有効になっている場合にだけ使える


```config:arch/arm/crypto/Kconfig

menuconfig ARM_CRYPTO
	bool "ARM Accelerated Cryptographic Algorithms"
	depends on ARM
	help
	  Say Y here to choose from a selection of cryptographic algorithms
	  implemented using ARM specific CPU features or instructions.

if ARM_CRYPTO

＜略＞

config CRYPTO_SHA1_ARM_NEON
	tristate "SHA1 digest algorithm (ARM NEON)"
	depends on KERNEL_MODE_NEON
	select CRYPTO_SHA1_ARM
	select CRYPTO_SHA1
	select CRYPTO_HASH
	help
	  SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2) implemented
	  using optimized ARM NEON assembly, when NEON instructions are
	  available.

```

# Makefile

KConfigで有効にした設定に応じて、必要なファイルを取り込むいつもの記載ですね

```makefile:arch/arm/crypto/Makefile

obj-$(CONFIG_CRYPTO_AES_ARM) += aes-arm.o
obj-$(CONFIG_CRYPTO_AES_ARM_BS) += aes-arm-bs.o
obj-$(CONFIG_CRYPTO_SHA1_ARM) += sha1-arm.o
obj-$(CONFIG_CRYPTO_SHA1_ARM_NEON) += sha1-arm-neon.o
obj-$(CONFIG_CRYPTO_SHA256_ARM) += sha256-arm.o

＜略＞
aes-arm-y	:= aes-cipher-core.o aes-cipher-glue.o
aes-arm-bs-y	:= aes-neonbs-core.o aes-neonbs-glue.o
sha1-arm-y	:= sha1-armv4-large.o sha1_glue.o
sha1-arm-neon-y	:= sha1-armv7-neon.o sha1_neon_glue.o
sha256-arm-neon-$(CONFIG_KERNEL_MODE_NEON) := sha256_neon_glue.o

```

# sha1-arm-neon.o

これは、 `sha1-arm-neon-y	:= sha1-armv7-neon.o sha1_neon_glue.o` の記載から、２つのファイルを結合したものになる。

## sha1_neon_glue.c

### モジュール組み込み時の、_init/_exit

モジュールを組み込んだ時点で、sha1_neon_mode_[init|fini] が登録される。

```c:arch/arm/crypto/sha1_neon_glue.c
module_init(sha1_neon_mod_init);
module_exit(sha1_neon_mod_fini);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SHA1 Secure Hash Algorithm, NEON accelerated");
MODULE_ALIAS_CRYPTO("sha1");

```

### sha1_neon_mod_init()

sha1_neon_mod_init()では、neon用の実装を登録している。
ここで、 `.init` は`sha1_base_init`となって共通実装を使い、その他は、`sha1_neon_*` と独自実装となっている点に注目されたい。

```c:arch/arm/crypto/sha1_neon_glue.c

static struct shash_alg alg = {
	.digestsize	=	SHA1_DIGEST_SIZE,
	.init		=	sha1_base_init,
	.update		=	sha1_neon_update,
	.final		=	sha1_neon_final,
	.finup		=	sha1_neon_finup,
	.descsize	=	sizeof(struct sha1_state),
	.base		=	{
		.cra_name		= "sha1",
		.cra_driver_name	= "sha1-neon",
		.cra_priority		= 250,
		.cra_blocksize		= SHA1_BLOCK_SIZE,
		.cra_module		= THIS_MODULE,
	}
};

static int __init sha1_neon_mod_init(void)
{
	if (!cpu_has_neon())
		return -ENODEV;

	return crypto_register_shash(&alg);
}

```

### sha1_neon_update()

```c:arch/arm/crypto/sha1_neon_glue.c
static int sha1_neon_update(struct shash_desc *desc, const u8 *data,
			  unsigned int len)
{
	struct sha1_state *sctx = shash_desc_ctx(desc);

	if (!crypto_simd_usable() ||
	    (sctx->count % SHA1_BLOCK_SIZE) + len < SHA1_BLOCK_SIZE)
		return sha1_update_arm(desc, data, len);

	kernel_neon_begin();
	sha1_base_do_update(desc, data, len,
			    (sha1_block_fn *)sha1_transform_neon);
	kernel_neon_end();

	return 0;
}
```

```c:arch/arm/crypto/sha1_glue.c
int sha1_update_arm(struct shash_desc *desc, const u8 *data,
		    unsigned int len)
{
	/* make sure casting to sha1_block_fn() is safe */
	BUILD_BUG_ON(offsetof(struct sha1_state, state) != 0);

	return sha1_base_do_update(desc, data, len,
				   (sha1_block_fn *)sha1_block_data_order);
}
EXPORT_SYMBOL_GPL(sha1_update_arm);

```

ここで、

- crypt_simd_usable() は、SIMD実装をcryptで利用できるかどうかを判定する関数。 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/include/crypto/internal/simd.h#n64 あたりに実装がある。
- SIMD実装をcryptで使えない場合は、利用できない場合には、普通の`sha1_update_arm()`から`sha1_base_do_update()`を呼び出すフォールバック処理がなされる。
- `kernel_neon_begin()` と、`kernel_neon_end()`は、NEON Registerの保護のために行われる関数。
- 処理の実体は、`sha1_base_do_update()`の引数で渡された、`sha1_transform_neon()` で行われる。

### sha1_neon_finup() / sha1_neon_final()

終了処理をまとめて説明する。

- simdがcryptで使えなかったら、通常処理に戻す。
- kernel_neon_begin() でneon使えるようにする
- sha1_base_do_update(sha1_trasnfrom_neon) で、処理本体を呼び出す
- kernel_neon_end() でneon使用状態を戻す


```c:sha1_neon_glue.c
static int sha1_neon_finup(struct shash_desc *desc, const u8 *data,
			   unsigned int len, u8 *out)
{
	if (!crypto_simd_usable())
		return sha1_finup_arm(desc, data, len, out);

	kernel_neon_begin();
	if (len)
		sha1_base_do_update(desc, data, len,
				    (sha1_block_fn *)sha1_transform_neon);
	sha1_base_do_finalize(desc, (sha1_block_fn *)sha1_transform_neon);
	kernel_neon_end();

	return sha1_base_finish(desc, out);
}

static int sha1_neon_final(struct shash_desc *desc, u8 *out)
{
	return sha1_neon_finup(desc, NULL, 0, out);
}
``` 

## sha1-armv7-neon.S


### sha1_transform_neon()

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm/crypto/sha1-armv7-neon.S#n292

```c:sha1-armv7-neon.S

/*
 * Transform nblks*64 bytes (nblks*16 32-bit words) at DATA.
 *
 * unsigned int
 * sha1_transform_neon (void *ctx, const unsigned char *data,
 *                      unsigned int nblks)
 */
.align 3
ENTRY(sha1_transform_neon)
  /* input:
   *	r0: ctx, CTX
   *	r1: data (64*nblks bytes)
   *	r2: nblks
   */

  cmp RNBLKS, #0;
  beq .Ldo_nothing;

＜略＞
```

# まとめ

- `XXXX-glue.c`というファイルを追加して、module_init()内でtransform関数を登録しよう。
- transform関数は、callbackとして引き渡す形で使うので、そこを機にしよう。
- 前提条件を満たさず使えなかった場合のフォールバック処理もちゃんと入れよう。


以上となります。


# おまけ：現在(2020/5/30) でLinux Kernelのcrypt実装では、どんなものがサポートされているのか。

ざっくり、ファイル名から見ると下記がサポートされているっポイ。

## ARMの場合 
(☆ = x86にはなくて、ARMにはある）

- aes
- chacha
- crc32
- crct10dif
- curve25519
- ghash
- nhpoly1305
- poly1305
- sha1
- sha2　☆
- sha256
- sha512

## ARM64の場合

(☆ = x86にはなくて、ARM64にはある）

- aes
- chacha
- crct10dif
- ghash
- ngpoly1305
- poly1305
- sha1
- sha2
- sha3
- sha256
- sha512
- sm3 ☆


## x86の場合　

（★= ARMにはなくて、x86にある)

特徴：さすがにみんなが使っていることだけあって、サポートが熱く厚いなあ……

- aegis128　★
- aes
- blake2s　★
- blowfish　★
- camellia　★
- cast5　★
- cast6　★
- chacha
- crc32
- crct10dif
- curve25519　★
- des3　★
- ghash
- nhpoly1305
- poly1305
- serpent　★
- sha1
- sha256
- sha512
- twofish ★


## powerpcの場合

特徴：　う、うーん、、、これ、特徴が無いように見える。

- aes
- crc32
- crct10dif
- md5
- sha1
- sha256

## sparcの場合

特徴： MD5とかAES,DES,CAMELLIAとか、SHA1,SHA256,SHA512とか、専用命令あるっぽい！

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/sparc/crypto/opcodes.h

- camellia
- crc32c
- des
- md5
- sha1
- sha256
- sha512

## MIPS

- chacha
- crc32
- poly1305

## S390

- aes
- RNG
- crc32
- des
- ghash
- paes
- sha1
- sha3
- sha256
- sha512

## RISC-V

未実装！！ここにcommitすれば、あなたもLinux Kernel Developerとして名前が未来永劫記録されますよ！！やったね！！！