Raspberry Pi 4 で NSSH (Non-Secure SHell) をビルドして Cipher/Mac の影響を測定する

# Motivation
- Raspberry Pi 4 に Rockstor を Install してバックアップ取ってたら 20 [MB/s] とかで遅すぎた
- `top` 見てたら sshd-session の CPU 使用率が 100% に張り付いていたので、暗号処理とか MAC 処理に苦しんでいるのでは？と
- `nc` とか使えばいいじゃん、と思うかもしれないが、複数ファイルの処理とか、他の処理とのコンパチを考えると `ssh` を使えたほうが嬉しい
  - 個人的に `ssh/scp/rsync` をバックエンドにしてローカルキャッシュとかトランザクション処理を行う Python ライブラリを作っているので、その兼ね合いもある

# TL;DR
- Cipher も MAC も無効化すると 1GbE の上限 (113 MB/s) くらい出る
- Cipher で見ると以下の順で処理が軽い
  - none > chacha20-poly1305@openssh.com > aes128-ctr > aes192-ctr > aes256-ctr > aes128-gcm@openssh.com > aes256-gcm@openssh.com
- MAC で見ると以下の順で処理が軽い (Cipher に若干の依存がある)
  - none > umac-64-etm@openssh.com > umac-128-etm@openssh.com > umac-64@openssh.com > umac-128@openssh.com > hmac-sha2-512-etm@openssh.com > hmac-sha2-512 > hmac-sha1-etm@openssh.com > hmac-sha1 > hmac-sha2-256 > hmac-sha2-256-etm@openssh.com 



# OpenSSH のビルド
- `openssh-portable` を DL して、「暗復号無効」「MAC 無効」のバイナリを作る

## ソースの clone & checkout

```sh
% git clone https://github.com/openssh/openssh-portable
% cd openssh-portable
% git checkout V_9_9_P2
Note: switching to 'V_9_9_P2'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at d76b26751 autogenerated files for release
``` 

## Ciphers "none" 有効化

```sh
% git apply enable-ciphers-none.patch
```

<details><summary>enable-ciphers-none.patch</summary>

```diff
diff --git a/cipher.c b/cipher.c
index 7d6e7d8c6..a98f6e81d 100644
--- a/cipher.c
+++ b/cipher.c
@@ -121,8 +121,8 @@ cipher_alg_list(char sep, int auth_only)
        const struct sshcipher *c;
 
        for (c = ciphers; c->name != NULL; c++) {
-               if ((c->flags & CFLAG_INTERNAL) != 0)
-                       continue;
+               //if ((c->flags & CFLAG_INTERNAL) != 0)
+               //      continue;
                if (auth_only && c->auth_len == 0)
                        continue;
                if (ret != NULL)
@@ -224,7 +224,7 @@ ciphers_valid(const char *names)
        for ((p = strsep(&cp, CIPHER_SEP)); p && *p != '\0';
            (p = strsep(&cp, CIPHER_SEP))) {
                c = cipher_by_name(p);
-               if (c == NULL || (c->flags & CFLAG_INTERNAL) != 0) {
+               if (c == NULL) { //|| (c->flags & CFLAG_INTERNAL) != 0) {
                        free(cipher_list);
                        return 0;
                }
diff --git a/myproposal.h b/myproposal.h
index 3bdc2e955..d01704296 100644
--- a/myproposal.h
+++ b/myproposal.h
@@ -61,7 +61,8 @@
 #define        KEX_SERVER_ENCRYPT \
        "chacha20-poly1305@openssh.com," \
        "aes128-ctr,aes192-ctr,aes256-ctr," \
-       "aes128-gcm@openssh.com,aes256-gcm@openssh.com"
+       "aes128-gcm@openssh.com,aes256-gcm@openssh.com," \
+       "none"
 
 #define KEX_CLIENT_ENCRYPT KEX_SERVER_ENCRYPT

```

</details>


## MACs "none" 有効化

```sh
% git apply enable-mac-none.patch
```

<details><summary>enable-mac-none.patch</summary>

```diff
diff --git a/mac.c b/mac.c
index f3dda6692..45659f654 100644
--- a/mac.c
+++ b/mac.c
@@ -41,6 +41,7 @@
 
 #include "openbsd-compat/openssl-compat.h"
 
+#define SSH_NOMAC      -1      /* NO MAC */
 #define SSH_DIGEST     1       /* SSH_DIGEST_XXX */
 #define SSH_UMAC       2       /* UMAC (not integrated with OpenSSL) */
 #define SSH_UMAC128    3
@@ -76,6 +77,9 @@ static const struct macalg macs[] = {

+       /* NO MAC */
+       { "none",                               SSH_NOMAC, 0, 0, 0, 0, 0 },
+
        { NULL,                                 0, 0, 0, 0, 0, 0 }
 };
 
@@ -106,7 +110,12 @@ static int
 mac_setup_by_alg(struct sshmac *mac, const struct macalg *macalg)
 {
        mac->type = macalg->type;
-       if (mac->type == SSH_DIGEST) {
+       if (mac->type == SSH_NOMAC) {
+               mac->mac_len = 0;
+               mac->key_len = 0;
+               mac->hmac_ctx = NULL;
+               mac->umac_ctx = NULL;
+       } else if (mac->type == SSH_DIGEST) {
                if ((mac->hmac_ctx = ssh_hmac_start(macalg->alg)) == NULL)
                        return SSH_ERR_ALLOC_FAIL;
                mac->key_len = mac->mac_len = ssh_hmac_bytes(macalg->alg);
@@ -142,6 +151,8 @@ mac_init(struct sshmac *mac)
        if (mac->key == NULL)
                return SSH_ERR_INVALID_ARGUMENT;
        switch (mac->type) {
+       case SSH_NOMAC:
+               return 0;
        case SSH_DIGEST:
                if (mac->hmac_ctx == NULL ||
                    ssh_hmac_init(mac->hmac_ctx, mac->key, mac->key_len) < 0)
@@ -176,6 +187,9 @@ mac_compute(struct sshmac *mac, u_int32_t seqno,
                return SSH_ERR_INTERNAL_ERROR;
 
        switch (mac->type) {
+       case SSH_NOMAC:
+               memset(u.m, 0, sizeof(u.m));
+               break;
        case SSH_DIGEST:
                put_u32(b, seqno);
                /* reset HMAC context */
@@ -214,7 +228,9 @@ mac_check(struct sshmac *mac, u_int32_t seqno,
        u_char ourmac[SSH_DIGEST_MAX_LENGTH];
        int r;
 
-       if (mac->mac_len > mlen)
+       if (mac->type == SSH_NOMAC) {
+               return 0;
+       } else if (mac->mac_len > mlen)
                return SSH_ERR_INVALID_ARGUMENT;
        if ((r = mac_compute(mac, seqno, data, dlen,
            ourmac, sizeof(ourmac))) != 0)
diff --git a/myproposal.h b/myproposal.h
index 3bdc2e955..d01704296 100644
--- a/myproposal.h
+++ b/myproposal.h
@@ -75,7 +76,8 @@
        "umac-128@openssh.com," \
        "hmac-sha2-256," \
        "hmac-sha2-512," \
-       "hmac-sha1"
+       "hmac-sha1," \
+       "none"
 
 #define KEX_CLIENT_MAC KEX_SERVER_MAC
```

</details>




## sshd_config 編集 (listen port)
- 2222 番ポートで起動するように設定


```sh
% git apply listen-on-2222.patch
% grep "Port " sshd_config
Port 2222
```

<details><summary>listen-on-2222.patch</summary>

```diff
diff --git a/sshd_config b/sshd_config
index 36894ace5..24620675a 100644
--- a/sshd_config
+++ b/sshd_config
@@ -10,7 +10,7 @@
 # possible, but leave them commented.  Uncommented options override the
 # default value.
 
-#Port 22
+Port 2222
 #AddressFamily any
 #ListenAddress 0.0.0.0
 #ListenAddress ::
```

</details>


## ビルド

<details><summary>git diff</summary>

```diff
diff --git a/cipher.c b/cipher.c
index 7d6e7d8c6..a98f6e81d 100644
--- a/cipher.c
+++ b/cipher.c
@@ -121,8 +121,8 @@ cipher_alg_list(char sep, int auth_only)
        const struct sshcipher *c;
 
        for (c = ciphers; c->name != NULL; c++) {
-               if ((c->flags & CFLAG_INTERNAL) != 0)
-                       continue;
+               //if ((c->flags & CFLAG_INTERNAL) != 0)
+               //      continue;
                if (auth_only && c->auth_len == 0)
                        continue;
                if (ret != NULL)
@@ -224,7 +224,7 @@ ciphers_valid(const char *names)
        for ((p = strsep(&cp, CIPHER_SEP)); p && *p != '\0';
            (p = strsep(&cp, CIPHER_SEP))) {
                c = cipher_by_name(p);
-               if (c == NULL || (c->flags & CFLAG_INTERNAL) != 0) {
+               if (c == NULL) { //|| (c->flags & CFLAG_INTERNAL) != 0) {
                        free(cipher_list);
                        return 0;
                }
diff --git a/mac.c b/mac.c
index f3dda6692..45659f654 100644
--- a/mac.c
+++ b/mac.c
@@ -41,6 +41,7 @@
 
 #include "openbsd-compat/openssl-compat.h"
 
+#define SSH_NOMAC      -1      /* NO MAC */
 #define SSH_DIGEST     1       /* SSH_DIGEST_XXX */
 #define SSH_UMAC       2       /* UMAC (not integrated with OpenSSL) */
 #define SSH_UMAC128    3
@@ -76,6 +77,9 @@ static const struct macalg macs[] = {
        { "umac-64-etm@openssh.com",            SSH_UMAC, 0, 0, 128, 64, 1 },
        { "umac-128-etm@openssh.com",           SSH_UMAC128, 0, 0, 128, 128, 1 },
 
+       /* NO MAC */
+       { "none",                               SSH_NOMAC, 0, 0, 0, 0, 0 },
+
        { NULL,                                 0, 0, 0, 0, 0, 0 }
 };
 
@@ -106,7 +110,12 @@ static int
 mac_setup_by_alg(struct sshmac *mac, const struct macalg *macalg)
 {
        mac->type = macalg->type;
-       if (mac->type == SSH_DIGEST) {
+       if (mac->type == SSH_NOMAC) {
+               mac->mac_len = 0;
+               mac->key_len = 0;
+               mac->hmac_ctx = NULL;
+               mac->umac_ctx = NULL;
+       } else if (mac->type == SSH_DIGEST) {
                if ((mac->hmac_ctx = ssh_hmac_start(macalg->alg)) == NULL)
                        return SSH_ERR_ALLOC_FAIL;
                mac->key_len = mac->mac_len = ssh_hmac_bytes(macalg->alg);
@@ -142,6 +151,8 @@ mac_init(struct sshmac *mac)
        if (mac->key == NULL)
                return SSH_ERR_INVALID_ARGUMENT;
        switch (mac->type) {
+       case SSH_NOMAC:
+               return 0;       
        case SSH_DIGEST:
                if (mac->hmac_ctx == NULL ||
                    ssh_hmac_init(mac->hmac_ctx, mac->key, mac->key_len) < 0)
@@ -176,6 +187,9 @@ mac_compute(struct sshmac *mac, u_int32_t seqno,
                return SSH_ERR_INTERNAL_ERROR;
 
        switch (mac->type) {
+       case SSH_NOMAC:
+               memset(u.m, 0, sizeof(u.m));
+               break;
        case SSH_DIGEST:
                put_u32(b, seqno);
                /* reset HMAC context */
@@ -214,7 +228,9 @@ mac_check(struct sshmac *mac, u_int32_t seqno,
        u_char ourmac[SSH_DIGEST_MAX_LENGTH];
        int r;
 
-       if (mac->mac_len > mlen)
+       if (mac->type == SSH_NOMAC) {
+               return 0;
+       } else if (mac->mac_len > mlen)
                return SSH_ERR_INVALID_ARGUMENT;
        if ((r = mac_compute(mac, seqno, data, dlen,
            ourmac, sizeof(ourmac))) != 0)
diff --git a/myproposal.h b/myproposal.h
index 3bdc2e955..d01704296 100644
--- a/myproposal.h
+++ b/myproposal.h
@@ -61,7 +61,8 @@
 #define        KEX_SERVER_ENCRYPT \
        "chacha20-poly1305@openssh.com," \
        "aes128-ctr,aes192-ctr,aes256-ctr," \
-       "aes128-gcm@openssh.com,aes256-gcm@openssh.com"
+       "aes128-gcm@openssh.com,aes256-gcm@openssh.com," \
+       "none"
 
 #define KEX_CLIENT_ENCRYPT KEX_SERVER_ENCRYPT
 
@@ -75,7 +76,8 @@
        "umac-128@openssh.com," \
        "hmac-sha2-256," \
        "hmac-sha2-512," \
-       "hmac-sha1"
+       "hmac-sha1," \
+       "none"
 
 #define KEX_CLIENT_MAC KEX_SERVER_MAC
 
diff --git a/sshd_config b/sshd_config
index 36894ace5..24620675a 100644
--- a/sshd_config
+++ b/sshd_config
@@ -10,7 +10,7 @@
 # possible, but leave them commented.  Uncommented options override the
 # default value.
 
-#Port 22
+Port 2222
 #AddressFamily any
 #ListenAddress 0.0.0.0
 #ListenAddress ::
```

</details>

```sh
% sudo apt install automake autoconf libssl-dev

% INSTALLDIR=/opt/nssh
% sudo rm -rf ${INSTALLDIR}
% sudo mkdir -p ${INSTALLDIR}
% autoreconf
% ./configure --prefix=${INSTALLDIR}
% make
% sudo make install
```

```sh
% tree /opt/nssh
/opt/nssh
├── bin
│   ├── scp
│   ├── sftp
│   ├── ssh
│   ├── ssh-add
│   ├── ssh-agent
│   ├── ssh-keygen
│   └── ssh-keyscan
├── etc
│   ├── moduli
│   ├── ssh_config
│   ├── ssh_host_ecdsa_key
│   ├── ssh_host_ecdsa_key.pub
│   ├── ssh_host_ed25519_key
│   ├── ssh_host_ed25519_key.pub
│   ├── ssh_host_rsa_key
│   ├── ssh_host_rsa_key.pub
│   └── sshd_config
├── lib
│   ├── sftp-server
│   ├── ssh-keysign
│   ├── ssh-pkcs11-helper
│   ├── ssh-sk-helper
│   └── sshd-session
├── sbin
│   └── sshd
└── share
    └── man
        ├── cat1
        │   ├── scp.1
        │   ├── sftp.1
        │   ├── ssh-add.1
        │   ├── ssh-agent.1
        │   ├── ssh-keygen.1
        │   ├── ssh-keyscan.1
        │   └── ssh.1
        ├── cat5
        │   ├── moduli.5
        │   ├── ssh_config.5
        │   └── sshd_config.5
        └── cat8
            ├── sftp-server.8
            ├── ssh-keysign.8
            ├── ssh-pkcs11-helper.8
            ├── ssh-sk-helper.8
            └── sshd.8

9 directories, 37 files
```


## sshd 起動
- `sshd` は絶対パスで指定される必要アリ

```sh
% sudo /opt/nssh/sbin/sshd -f /opt/nssh/etc/sshd_config
```



# 性能測定
- サーバ側 `$`
  - Raspberry Pi4 / 8GB
  - nssh (`openssh-portable V_9_9_P2`)
- クライアント側 `%`
  - NUC8i3BEH
  - nssh (`openssh-portable V_9_9_P2`)
- 計測コマンド

```sh
% dd if=/dev/random of=/tmp/in.img bs=4K count=512K
% cat /tmp/in.img | /opt/nssh/bin/ssh -c <cipher> -m <mac> -p 2222 rpi4 'dd of=/dev/null'
```


- 計測結果

![graph.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278058/50af0729-e16d-4acd-8839-2d90746abbc7.jpeg)




| cipher | mac | Throughput (min) [MB/s] | Throughput (mean) [MB/s] |  Throughput (MAX) [MB/s] |
|:-:|:-:|-:|-:|-:|
| aes128-ctr | hmac-sha1 | 45.2 | 50.08 | 60.9 |
| aes128-ctr | hmac-sha2-256 | 46.0 | 49.02 | 51.3 |
| aes128-ctr | hmac-sha2-512 | 48.3 | 55.58 | 60.8 |
| aes128-ctr | umac-64@openssh.com | 66.5 | 73.18 | 77.7 |
| aes128-ctr | umac-128@openssh.com | 60.2 | 70.92 | 77.7 |
| aes128-ctr | hmac-sha1-etm@openssh.com | 50.1 | 54.94 | 60.5 |
| aes128-ctr | hmac-sha2-256-etm@openssh.com | 42.7 | 47.66 | 54.9 |
| aes128-ctr | hmac-sha2-512-etm@openssh.com | 52.6 | 56.9 | 60.6 |
| aes128-ctr | umac-64-etm@openssh.com | 68.0 | 77.6 | 81.2 |
| aes128-ctr | umac-128-etm@openssh.com | 59.4 | 73.78 | 79.6 |
| aes128-ctr | none | 81.3 | 83.62 | 85.0 |
| aes192-ctr | hmac-sha1 | 50.0 | 54.68 | 56.8 |
| aes192-ctr | hmac-sha2-256 | 46.1 | 48.98 | 51.3 |
| aes192-ctr | hmac-sha2-512 | 43.2 | 50.12 | 55.4 |
| aes192-ctr | umac-64@openssh.com | 64.8 | 69.32 | 72.6 |
| aes192-ctr | umac-128@openssh.com | 64.8 | 67.46 | 69.4 |
| aes192-ctr | hmac-sha1-etm@openssh.com | 49.0 | 52.58 | 56.4 |
| aes192-ctr | hmac-sha2-256-etm@openssh.com | 43.8 | 47.7 | 51.0 |
| aes192-ctr | hmac-sha2-512-etm@openssh.com | 52.9 | 54.12 | 55.5 |
| aes192-ctr | umac-64-etm@openssh.com | 60.5 | 65.96 | 70.8 |
| aes192-ctr | umac-128-etm@openssh.com | 54.3 | 64.08 | 69.1 |
| aes192-ctr | none | 70.0 | 73.82 | 75.8 |
| aes256-ctr | hmac-sha1 | 48.2 | 49 | 50.4 |
| aes256-ctr | hmac-sha2-256 | 43.0 | 44.18 | 45.5 |
| aes256-ctr | hmac-sha2-512 | 44.3 | 50.7 | 54.7 |
| aes256-ctr | umac-64@openssh.com | 50.7 | 57.6 | 63.4 |
| aes256-ctr | umac-128@openssh.com | 58.5 | 61.48 | 63.8 |
| aes256-ctr | hmac-sha1-etm@openssh.com | 44.9 | 48.86 | 50.7 |
| aes256-ctr | hmac-sha2-256-etm@openssh.com | 37.7 | 43.66 | 47.6 |
| aes256-ctr | hmac-sha2-512-etm@openssh.com | 47.7 | 49.78 | 51.6 |
| aes256-ctr | umac-64-etm@openssh.com | 54.5 | 59.54 | 63.0 |
| aes256-ctr | umac-128-etm@openssh.com | 57.1 | 59.4 | 63.5 |
| aes256-ctr | none | 59.3 | 63.16 | 66.7 |
| aes128-gcm@openssh.com | hmac-sha1 | 46.8 | 48.36 | 50.2 |
| aes128-gcm@openssh.com | hmac-sha2-256 | 45.6 | 48.74 | 51.1 |
| aes128-gcm@openssh.com | hmac-sha2-512 | 48.8 | 49.72 | 50.4 |
| aes128-gcm@openssh.com | umac-64@openssh.com | 48.0 | 48.82 | 50.5 |
| aes128-gcm@openssh.com | umac-128@openssh.com | 46.7 | 49.22 | 50.6 |
| aes128-gcm@openssh.com | hmac-sha1-etm@openssh.com | 46.6 | 48.54 | 49.9 |
| aes128-gcm@openssh.com | hmac-sha2-256-etm@openssh.com | 44.8 | 48.94 | 50.3 |
| aes128-gcm@openssh.com | hmac-sha2-512-etm@openssh.com | 46.9 | 48.5 | 49.5 |
| aes128-gcm@openssh.com | umac-64-etm@openssh.com | 40.6 | 47.18 | 50.7 |
| aes128-gcm@openssh.com | umac-128-etm@openssh.com | 48.1 | 49.24 | 50.3 |
| aes128-gcm@openssh.com | none | 45.0 | 47.86 | 50.4 |
| aes256-gcm@openssh.com | hmac-sha1 | 41.9 | 43.24 | 44.1 |
| aes256-gcm@openssh.com | hmac-sha2-256 | 42.8 | 43.9 | 44.7 |
| aes256-gcm@openssh.com | hmac-sha2-512 | 40.7 | 42.36 | 43.8 |
| aes256-gcm@openssh.com | umac-64@openssh.com | 40.5 | 42.66 | 44.3 |
| aes256-gcm@openssh.com | umac-128@openssh.com | 41.3 | 43.2 | 44.1 |
| aes256-gcm@openssh.com | hmac-sha1-etm@openssh.com | 42.0 | 43.46 | 44.4 |
| aes256-gcm@openssh.com | hmac-sha2-256-etm@openssh.com | 40.9 | 43.2 | 44.4 |
| aes256-gcm@openssh.com | hmac-sha2-512-etm@openssh.com | 40.8 | 42.7 | 44.4 |
| aes256-gcm@openssh.com | umac-64-etm@openssh.com | 40.1 | 42.5 | 44.5 |
| aes256-gcm@openssh.com | umac-128-etm@openssh.com | 41.4 | 43.64 | 44.8 |
| aes256-gcm@openssh.com | none | 39.0 | 42.42 | 44.9 |
| chacha20-poly1305@openssh.com | hmac-sha1 | 76.5 | 84.44 | 97.2 |
| chacha20-poly1305@openssh.com | hmac-sha2-256 | 77.1 | 81.52 | 87.3 |
| chacha20-poly1305@openssh.com | hmac-sha2-512 | 75.2 | 85.4 | 94.3 |
| chacha20-poly1305@openssh.com | umac-64@openssh.com | 78.1 | 89.14 | 102 |
| chacha20-poly1305@openssh.com | umac-128@openssh.com | 74.4 | 81.3 | 95.3 |
| chacha20-poly1305@openssh.com | hmac-sha1-etm@openssh.com | 79.8 | 84.22 | 92.2 |
| chacha20-poly1305@openssh.com | hmac-sha2-256-etm@openssh.com | 79.6 | 85.44 | 89.7 |
| chacha20-poly1305@openssh.com | hmac-sha2-512-etm@openssh.com | 72.6 | 78.94 | 83.5 |
| chacha20-poly1305@openssh.com | umac-64-etm@openssh.com | 74.7 | 83.08 | 89.7 |
| chacha20-poly1305@openssh.com | umac-128-etm@openssh.com | 75.7 | 83.02 | 89.9 |
| chacha20-poly1305@openssh.com | none | 78.8 | 83.22 | 85.7 |
| none | hmac-sha1 | 88.0 | 99.12 | 111 |
| none | hmac-sha2-256 | 82.1 | 89.64 | 94.1 |
| none | hmac-sha2-512 | 75.7 | 81.28 | 96.5 |
| none | umac-64@openssh.com | 89.1 | 93.52 | 95.7 |
| none | umac-128@openssh.com | 88.4 | 92.04 | 95.8 |
| none | hmac-sha1-etm@openssh.com | 91.9 | 98.8 | 103 |
| none | hmac-sha2-256-etm@openssh.com | 76.6 | 90.48 | 95.0 |
| none | hmac-sha2-512-etm@openssh.com | 77.7 | 80.82 | 82.9 |
| none | umac-64-etm@openssh.com | 87.2 | 91.08 | 95.2 |
| none | umac-128-etm@openssh.com | 90.2 | 92.96 | 100 |
| none | none | 95.4 | 102.84 | 113 |