Browse Source

Add support for more types of encrypted PEM files

1. ChaCha20, two-key 3DES and DES-X encrypted OpenSSL PEM files

2. AES-GCM and Chacha20+Poly1305 encrypted SSH keys

* OpenSSH uses a slightly different algorithm for its
  `[email protected]` than defined in the RFC.
  Therefore add an `openssh_compat` flag to
  `chacha20poly1305_state`.
* Add the option to give a 16byte IV and no counter, when calling
  `chacha20poly1305_memory()`
* Add support for DES-X

Signed-off-by: Steffen Jaeckel <[email protected]>
Steffen Jaeckel 1 year ago
parent
commit
c0be0aa0bf
51 changed files with 638 additions and 208 deletions
  1. 24 8
      demos/pem-info.c
  2. 97 79
      doc/crypt.tex
  3. 54 0
      notes/cipher_tv.txt
  4. 19 0
      notes/eax_tv.txt
  5. 19 0
      notes/ocb_tv.txt
  6. 19 0
      notes/omac_tv.txt
  7. 19 0
      notes/pmac_tv.txt
  8. 156 7
      src/ciphers/des.c
  9. 8 6
      src/encauth/chachapoly/chacha20poly1305_done.c
  10. 1 0
      src/encauth/chachapoly/chacha20poly1305_init.c
  11. 4 0
      src/encauth/chachapoly/chacha20poly1305_memory.c
  12. 13 1
      src/headers/tomcrypt_cipher.h
  13. 4 3
      src/headers/tomcrypt_mac.h
  14. 14 1
      src/headers/tomcrypt_private.h
  15. 2 0
      src/misc/crypt/crypt.c
  16. 1 0
      src/misc/crypt/crypt_register_all_ciphers.c
  17. 107 54
      src/misc/pem/pem.c
  18. 2 6
      src/misc/pem/pem_pkcs.c
  19. 57 38
      src/misc/pem/pem_ssh.c
  20. 15 5
      src/stream/chacha/chacha_memory.c
  21. 0 0
      tests/pem/pkcs/rsa-chacha20.pem
  22. 0 0
      tests/pem/pkcs/rsa-des-ede-cbc.pem
  23. 0 0
      tests/pem/pkcs/rsa-des-ede-cfb.pem
  24. 0 0
      tests/pem/pkcs/rsa-des-ede-ofb.pem
  25. 0 0
      tests/pem/pkcs/rsa-desx-cbc.pem
  26. 0 0
      tests/pem/pubkeys/authorized_keys/ssh_dsa_1024_openssh.pub
  27. 0 0
      tests/pem/pubkeys/authorized_keys/ssh_ecdsa_256_openssh.pub
  28. 0 0
      tests/pem/pubkeys/authorized_keys/ssh_ecdsa_384_openssh.pub
  29. 0 0
      tests/pem/pubkeys/authorized_keys/ssh_ecdsa_521_openssh.pub
  30. 0 0
      tests/pem/pubkeys/authorized_keys/ssh_ed25519_openssh.pub
  31. 0 0
      tests/pem/pubkeys/authorized_keys/ssh_rsa_1024_openssh.pub
  32. 0 0
      tests/pem/pubkeys/authorized_keys/ssh_rsa_1536_openssh.pub
  33. 0 0
      tests/pem/pubkeys/authorized_keys/ssh_rsa_2048_openssh.pub
  34. 0 0
      tests/pem/pubkeys/authorized_keys/ssh_rsa_4096_openssh.pub
  35. 0 0
      tests/pem/pubkeys/authorized_keys/ssh_rsa_768_openssh.pub
  36. 0 0
      tests/pem/pubkeys/authorized_keys/ssh_rsa_8192_openssh.pub
  37. 0 0
      tests/pem/ssh/authorized_keys/ssh-rsa-3des-cbc.pub
  38. 0 0
      tests/pem/ssh/authorized_keys/ssh-rsa-aes128-cbc.pub
  39. 0 0
      tests/pem/ssh/authorized_keys/ssh-rsa-aes128-ctr.pub
  40. 0 0
      tests/pem/ssh/authorized_keys/ssh-rsa-aes128-gcm.pub
  41. 0 0
      tests/pem/ssh/authorized_keys/ssh-rsa-aes192-cbc.pub
  42. 0 0
      tests/pem/ssh/authorized_keys/ssh-rsa-aes192-ctr.pub
  43. 0 0
      tests/pem/ssh/authorized_keys/ssh-rsa-aes256-cbc.pub
  44. 0 0
      tests/pem/ssh/authorized_keys/ssh-rsa-aes256-ctr.pub
  45. 0 0
      tests/pem/ssh/authorized_keys/ssh-rsa-aes256-gcm.pub
  46. 0 0
      tests/pem/ssh/authorized_keys/ssh-rsa-chacha20-poly1305.pub
  47. 0 0
      tests/pem/ssh/ssh-rsa-aes128-gcm
  48. 0 0
      tests/pem/ssh/ssh-rsa-aes256-gcm
  49. 0 0
      tests/pem/ssh/ssh-rsa-chacha20-poly1305
  50. 2 0
      tests/pem_test.c
  51. 1 0
      tests/test.c

+ 24 - 8
demos/pem-info.c

@@ -16,10 +16,13 @@ static const struct {
    { "", "none" },
    { "aes", "AES" },
    { "blowfish", "Blowfish" },
+   { "c20p1305", "ChaCha20Poly1305" },
    { "camellia", "Camellia" },
    { "cast5", "CAST5" },
+   { "chacha20", "ChaCha20" },
    { "3des", "3DES (EDE)" },
    { "des", "DES" },
+   { "desx", "DES-X" },
    { "idea", "IDEA" },
    { "rc5", "RC5" },
    { "rc2", "RC2" },
@@ -39,14 +42,27 @@ static const char *s_map_cipher(const char *name)
    exit(1);
 }
 
-static const char *cipher_mode_map[] = {
-   "none", "CBC", "CFB", "CTR", "OFB", "STREAM", "GCM"
+static const struct {
+   enum cipher_mode mode;
+   const char *name;
+} cipher_mode_map[] = {
+   { cm_none,   "none",   },
+   { cm_cbc,    "CBC",    },
+   { cm_cfb,    "CFB",    },
+   { cm_ctr,    "CTR",    },
+   { cm_ofb,    "OFB",    },
+   { cm_stream, "STREAM", },
+   { cm_gcm,    "GCM",    },
 };
 
 static const char *s_map_mode(enum cipher_mode mode)
 {
-   if (mode >= 0 && mode <= sizeof(cipher_mode_map)/sizeof(cipher_mode_map[0]))
-      return cipher_mode_map[mode];
+   size_t n;
+   mode &= cm_modes;
+   for (n = 0; n < sizeof(cipher_mode_map)/sizeof(cipher_mode_map[0]); ++n) {
+      if (cipher_mode_map[n].mode == mode)
+         return cipher_mode_map[n].name;
+   }
    fprintf(stderr, "Error: Can't map cipher_mode %d\n", mode);
    exit(1);
 }
@@ -56,11 +72,11 @@ int main(void)
    unsigned long n;
    printf("PEM ciphers:\n\n");
    for (n = 0; n < pem_dek_infos_num; ++n) {
-      char nbuf[20] = {0};
+      char nbuf[32] = {0};
       size_t nlen = strlen(pem_dek_infos[n].name);
       memcpy(nbuf, pem_dek_infos[n].name, nlen);
       nbuf[nlen-1] = '}';
-      printf("\\hline \\texttt{%-18s & %-15s & %-25ld & %s \\\\\n",
+      printf("\\hline \\texttt{%-18s & %-15s & %-25ld & %-6s \\\\\n",
                                nbuf, s_map_cipher(pem_dek_infos[n].algo),
                                               pem_dek_infos[n].keylen * 8,
                                                        s_map_mode(pem_dek_infos[n].mode));
@@ -68,11 +84,11 @@ int main(void)
 
    printf("\nSSH ciphers:\n\n");
    for (n = 0; n < ssh_ciphers_num; ++n) {
-      char nbuf[20] = {0};
+      char nbuf[32] = {0};
       size_t nlen = strlen(ssh_ciphers[n].name);
       memcpy(nbuf, ssh_ciphers[n].name, nlen);
       nbuf[nlen] = '}';
-      printf("\\hline \\texttt{%-18s & %-15s & %-25ld & %-4s \\\\\n",
+      printf("\\hline \\texttt{%-30s & %-16s & %-24ld & %-6s \\\\\n",
                                nbuf, s_map_cipher(ssh_ciphers[n].algo),
                                ssh_ciphers[n].keylen * 8,
                                                        s_map_mode(ssh_ciphers[n].mode));

+ 97 - 79
doc/crypt.tex

@@ -2422,9 +2422,15 @@ int main(void)
 \end{small}
 
 \mysection{ChaCha20--Poly1305}
+\label{chacha20poly1305}
 
-This authenticated encryption is based on ChaCha20 stream cipher and Poly1305 authenticator.
-It is defined by \url{https://tools.ietf.org/html/rfc7539}.
+This authenticated encryption is based on the ChaCha20 stream cipher and Poly1305 authenticator.
+It is defined by \url{https://tools.ietf.org/html/rfc8439}.
+
+IMPORTANT NOTICE: The Chacha20--Poly1305 implementation of LibTomCrypt is compliant to \textbf{RFC8439}, which differs slightly
+to what OpenSSH defined as \textbf{[email protected]}. The OpenSSH compatibility mode can be enabled
+by setting the \textit{openssh\_compat} element of \textit{chacha20poly1305\_state} to \textit{1},
+after calling \textit{chacha20poly1305\_init()}.
 
 \subsection{Initialization}
 To initialize the ChaCha20--Poly1305 context with a secret key call the following function.
@@ -2529,9 +2535,13 @@ encrypt (\textit{direction} equals \textbf{CHACHA20POLY1305\_ENCRYPT}) or decryp
 \textbf{CHACHA20POLY1305\_DECRYPT}) the message text and store the final message tag. The definition of the
 variables is the same as it is for all the manual functions.
 
-IMPORTANT NOTICE: for \textit{direction == CHACHA20POLY1305\_DECRYPT} the caller has to fill \textit{tag} and \textit{taglen} with expected
+IMPORTANT NOTICE 1: for \textit{direction == CHACHA20POLY1305\_DECRYPT} the caller has to fill \textit{tag} and \textit{taglen} with expected
 tag value. The \textit{chacha20poly1305\_memory} in decrypt mode validates the \textit{tag} value and returns \textit{CRYPT\_ERROR} on mismatch.
 
+IMPORTANT NOTICE 2: As mentioned in \ref{chacha20poly1305} there exists a discrepancy between the RFC and OpenSSH versions of the algorithm.
+In order to enable OpenSSH compatibility, the flag \textit{CHACHA20POLY1305\_OPENSSH\_COMPAT} has to be \textbf{OR}'ed into
+the \textit{direction} parameter.
+
 \chapter{One-Way Cryptographic Hash Functions}
 \mysection{Core Functions}
 Like the ciphers, there are hash core functions and a universal data type to hold the hash state called \textit{hash\_state}.  To initialize hash
@@ -7563,56 +7573,61 @@ When dealing with PEM formatted private keys the following encryption algorithms
 \begin{small}
 \begin{tabular}{|l|l|l|l|}
 \hline \textbf{Identifier}        & \textbf{Cipher} & \textbf{Key size in bits} & \textbf{Mode} \\
-\hline \texttt{AES-128-CBC}       & AES             & 128                       & CBC \\
-\hline \texttt{AES-192-CBC}       & AES             & 192                       & CBC \\
-\hline \texttt{AES-256-CBC}       & AES             & 256                       & CBC \\
-\hline \texttt{AES-128-CFB}       & AES             & 128                       & CFB \\
-\hline \texttt{AES-192-CFB}       & AES             & 192                       & CFB \\
-\hline \texttt{AES-256-CFB}       & AES             & 256                       & CFB \\
-\hline \texttt{AES-128-CTR}       & AES             & 128                       & CTR \\
-\hline \texttt{AES-192-CTR}       & AES             & 192                       & CTR \\
-\hline \texttt{AES-256-CTR}       & AES             & 256                       & CTR \\
-\hline \texttt{AES-128-OFB}       & AES             & 128                       & OFB \\
-\hline \texttt{AES-192-OFB}       & AES             & 192                       & OFB \\
-\hline \texttt{AES-256-OFB}       & AES             & 256                       & OFB \\
-\hline \texttt{BF-CBC}            & Blowfish        & 128                       & CBC \\
-\hline \texttt{BF-CFB}            & Blowfish        & 128                       & CFB \\
-\hline \texttt{BF-OFB}            & Blowfish        & 128                       & OFB \\
-\hline \texttt{CAMELLIA-128-CBC}  & Camellia        & 128                       & CBC \\
-\hline \texttt{CAMELLIA-192-CBC}  & Camellia        & 192                       & CBC \\
-\hline \texttt{CAMELLIA-256-CBC}  & Camellia        & 256                       & CBC \\
-\hline \texttt{CAMELLIA-128-CFB}  & Camellia        & 128                       & CFB \\
-\hline \texttt{CAMELLIA-192-CFB}  & Camellia        & 192                       & CFB \\
-\hline \texttt{CAMELLIA-256-CFB}  & Camellia        & 256                       & CFB \\
-\hline \texttt{CAMELLIA-128-CTR}  & Camellia        & 128                       & CTR \\
-\hline \texttt{CAMELLIA-192-CTR}  & Camellia        & 192                       & CTR \\
-\hline \texttt{CAMELLIA-256-CTR}  & Camellia        & 256                       & CTR \\
-\hline \texttt{CAMELLIA-128-OFB}  & Camellia        & 128                       & OFB \\
-\hline \texttt{CAMELLIA-192-OFB}  & Camellia        & 192                       & OFB \\
-\hline \texttt{CAMELLIA-256-OFB}  & Camellia        & 256                       & OFB \\
-\hline \texttt{CAST5-CBC}         & CAST5           & 128                       & CBC \\
-\hline \texttt{CAST5-CFB}         & CAST5           & 128                       & CFB \\
-\hline \texttt{CAST5-OFB}         & CAST5           & 128                       & OFB \\
-\hline \texttt{DES-EDE3-CBC}      & 3DES (EDE)      & 192                       & CBC \\
-\hline \texttt{DES-EDE3-CFB}      & 3DES (EDE)      & 192                       & CFB \\
-\hline \texttt{DES-EDE3-OFB}      & 3DES (EDE)      & 192                       & OFB \\
-\hline \texttt{DES-CBC}           & DES             & 64                        & CBC \\
-\hline \texttt{DES-CFB}           & DES             & 64                        & CFB \\
-\hline \texttt{DES-OFB}           & DES             & 64                        & OFB \\
-\hline \texttt{IDEA-CBC}          & IDEA            & 128                       & CBC \\
-\hline \texttt{IDEA-CFB}          & IDEA            & 128                       & CFB \\
-\hline \texttt{IDEA-OFB}          & IDEA            & 128                       & OFB \\
-\hline \texttt{RC5-CBC}           & RC5             & 128                       & CBC \\
-\hline \texttt{RC5-CFB}           & RC5             & 128                       & CFB \\
-\hline \texttt{RC5-OFB}           & RC5             & 128                       & OFB \\
-\hline \texttt{RC2-40-CBC}        & RC2             & 40                        & CBC \\
-\hline \texttt{RC2-64-CBC}        & RC2             & 64                        & CBC \\
-\hline \texttt{RC2-CBC}           & RC2             & 128                       & CBC \\
-\hline \texttt{RC2-CFB}           & RC2             & 128                       & CFB \\
-\hline \texttt{RC2-OFB}           & RC2             & 128                       & OFB \\
-\hline \texttt{SEED-CBC}          & SEED            & 128                       & CBC \\
-\hline \texttt{SEED-CFB}          & SEED            & 128                       & CFB \\
-\hline \texttt{SEED-OFB}          & SEED            & 128                       & OFB \\
+\hline \texttt{AES-128-CBC}       & AES             & 128                       & CBC    \\
+\hline \texttt{AES-192-CBC}       & AES             & 192                       & CBC    \\
+\hline \texttt{AES-256-CBC}       & AES             & 256                       & CBC    \\
+\hline \texttt{AES-128-CFB}       & AES             & 128                       & CFB    \\
+\hline \texttt{AES-192-CFB}       & AES             & 192                       & CFB    \\
+\hline \texttt{AES-256-CFB}       & AES             & 256                       & CFB    \\
+\hline \texttt{AES-128-CTR}       & AES             & 128                       & CTR    \\
+\hline \texttt{AES-192-CTR}       & AES             & 192                       & CTR    \\
+\hline \texttt{AES-256-CTR}       & AES             & 256                       & CTR    \\
+\hline \texttt{AES-128-OFB}       & AES             & 128                       & OFB    \\
+\hline \texttt{AES-192-OFB}       & AES             & 192                       & OFB    \\
+\hline \texttt{AES-256-OFB}       & AES             & 256                       & OFB    \\
+\hline \texttt{BF-CBC}            & Blowfish        & 128                       & CBC    \\
+\hline \texttt{BF-CFB}            & Blowfish        & 128                       & CFB    \\
+\hline \texttt{BF-OFB}            & Blowfish        & 128                       & OFB    \\
+\hline \texttt{CAMELLIA-128-CBC}  & Camellia        & 128                       & CBC    \\
+\hline \texttt{CAMELLIA-192-CBC}  & Camellia        & 192                       & CBC    \\
+\hline \texttt{CAMELLIA-256-CBC}  & Camellia        & 256                       & CBC    \\
+\hline \texttt{CAMELLIA-128-CFB}  & Camellia        & 128                       & CFB    \\
+\hline \texttt{CAMELLIA-192-CFB}  & Camellia        & 192                       & CFB    \\
+\hline \texttt{CAMELLIA-256-CFB}  & Camellia        & 256                       & CFB    \\
+\hline \texttt{CAMELLIA-128-CTR}  & Camellia        & 128                       & CTR    \\
+\hline \texttt{CAMELLIA-192-CTR}  & Camellia        & 192                       & CTR    \\
+\hline \texttt{CAMELLIA-256-CTR}  & Camellia        & 256                       & CTR    \\
+\hline \texttt{CAMELLIA-128-OFB}  & Camellia        & 128                       & OFB    \\
+\hline \texttt{CAMELLIA-192-OFB}  & Camellia        & 192                       & OFB    \\
+\hline \texttt{CAMELLIA-256-OFB}  & Camellia        & 256                       & OFB    \\
+\hline \texttt{CAST5-CBC}         & CAST5           & 128                       & CBC    \\
+\hline \texttt{CAST5-CFB}         & CAST5           & 128                       & CFB    \\
+\hline \texttt{CAST5-OFB}         & CAST5           & 128                       & OFB    \\
+\hline \texttt{ChaCha20}          & ChaCha20        & 256                       & STREAM \\
+\hline \texttt{DES-EDE-CBC}       & 3DES (EDE)      & 128                       & CBC    \\
+\hline \texttt{DES-EDE-CFB}       & 3DES (EDE)      & 128                       & CFB    \\
+\hline \texttt{DES-EDE-OFB}       & 3DES (EDE)      & 128                       & OFB    \\
+\hline \texttt{DES-EDE3-CBC}      & 3DES (EDE)      & 192                       & CBC    \\
+\hline \texttt{DES-EDE3-CFB}      & 3DES (EDE)      & 192                       & CFB    \\
+\hline \texttt{DES-EDE3-OFB}      & 3DES (EDE)      & 192                       & OFB    \\
+\hline \texttt{DES-CBC}           & DES             & 64                        & CBC    \\
+\hline \texttt{DES-CFB}           & DES             & 64                        & CFB    \\
+\hline \texttt{DES-OFB}           & DES             & 64                        & OFB    \\
+\hline \texttt{DESX-CBC}          & DES-X           & 192                       & CBC    \\
+\hline \texttt{IDEA-CBC}          & IDEA            & 128                       & CBC    \\
+\hline \texttt{IDEA-CFB}          & IDEA            & 128                       & CFB    \\
+\hline \texttt{IDEA-OFB}          & IDEA            & 128                       & OFB    \\
+\hline \texttt{RC5-CBC}           & RC5             & 128                       & CBC    \\
+\hline \texttt{RC5-CFB}           & RC5             & 128                       & CFB    \\
+\hline \texttt{RC5-OFB}           & RC5             & 128                       & OFB    \\
+\hline \texttt{RC2-40-CBC}        & RC2             & 40                        & CBC    \\
+\hline \texttt{RC2-64-CBC}        & RC2             & 64                        & CBC    \\
+\hline \texttt{RC2-CBC}           & RC2             & 128                       & CBC    \\
+\hline \texttt{RC2-CFB}           & RC2             & 128                       & CFB    \\
+\hline \texttt{RC2-OFB}           & RC2             & 128                       & OFB    \\
+\hline \texttt{SEED-CBC}          & SEED            & 128                       & CBC    \\
+\hline \texttt{SEED-CFB}          & SEED            & 128                       & CFB    \\
+\hline \texttt{SEED-OFB}          & SEED            & 128                       & OFB    \\
 \hline
 \end{tabular}
 \end{small}
@@ -7657,32 +7672,35 @@ When dealing with SSH formatted private keys the following encryption algorithms
 \begin{table}[H]
 \begin{small}
 \begin{tabular}{|l|l|l|l|}
-\hline \textbf{Identifier}        & \textbf{Cipher} & \textbf{Key size in bits} & \textbf{Mode} \\
-\hline \texttt{none}              & none            & 0                         & none \\
-\hline \texttt{aes128-cbc}        & AES             & 128                       & CBC  \\
-\hline \texttt{aes128-ctr}        & AES             & 128                       & CTR  \\
-\hline \texttt{aes192-cbc}        & AES             & 192                       & CBC  \\
-\hline \texttt{aes192-ctr}        & AES             & 192                       & CTR  \\
-\hline \texttt{aes256-cbc}        & AES             & 256                       & CBC  \\
-\hline \texttt{aes256-ctr}        & AES             & 256                       & CTR  \\
-\hline \texttt{blowfish128-cbc}   & Blowfish        & 128                       & CBC  \\
-\hline \texttt{blowfish128-ctr}   & Blowfish        & 128                       & CTR  \\
-\hline \texttt{des-cbc}           & DES             & 64                        & CBC  \\
-\hline \texttt{3des-cbc}          & 3DES (EDE)      & 192                       & CBC  \\
-\hline \texttt{3des-ctr}          & 3DES (EDE)      & 192                       & CTR  \\
-\hline \texttt{serpent128-cbc}    & Serpent         & 128                       & CBC  \\
-\hline \texttt{serpent128-ctr}    & Serpent         & 128                       & CTR  \\
-\hline \texttt{serpent192-cbc}    & Serpent         & 192                       & CBC  \\
-\hline \texttt{serpent192-ctr}    & Serpent         & 192                       & CTR  \\
-\hline \texttt{serpent256-cbc}    & Serpent         & 256                       & CBC  \\
-\hline \texttt{serpent256-ctr}    & Serpent         & 256                       & CTR  \\
-\hline \texttt{twofish128-cbc}    & Twofish         & 128                       & CBC  \\
-\hline \texttt{twofish128-ctr}    & Twofish         & 128                       & CTR  \\
-\hline \texttt{twofish192-cbc}    & Twofish         & 192                       & CBC  \\
-\hline \texttt{twofish192-ctr}    & Twofish         & 192                       & CTR  \\
-\hline \texttt{twofish-cbc}       & Twofish         & 256                       & CBC  \\
-\hline \texttt{twofish256-cbc}    & Twofish         & 256                       & CBC  \\
-\hline \texttt{twofish256-ctr}    & Twofish         & 256                       & CTR  \\
+\hline \textbf{Identifier}                    & \textbf{Cipher} & \textbf{Key size in bits} & \textbf{Mode} \\
+\hline \texttt{none}                          & none             & 0                        & none   \\
+\hline \texttt{aes128-cbc}                    & AES              & 128                      & CBC    \\
+\hline \texttt{aes128-ctr}                    & AES              & 128                      & CTR    \\
+\hline \texttt{aes192-cbc}                    & AES              & 192                      & CBC    \\
+\hline \texttt{aes192-ctr}                    & AES              & 192                      & CTR    \\
+\hline \texttt{aes256-cbc}                    & AES              & 256                      & CBC    \\
+\hline \texttt{aes256-ctr}                    & AES              & 256                      & CTR    \\
+\hline \texttt{[email protected]}        & AES              & 128                      & GCM    \\
+\hline \texttt{[email protected]}        & AES              & 256                      & GCM    \\
+\hline \texttt{blowfish128-cbc}               & Blowfish         & 128                      & CBC    \\
+\hline \texttt{blowfish128-ctr}               & Blowfish         & 128                      & CTR    \\
+\hline \texttt{[email protected]} & ChaCha20Poly1305 & 256                      & STREAM \\
+\hline \texttt{des-cbc}                       & DES              & 64                       & CBC    \\
+\hline \texttt{3des-cbc}                      & 3DES (EDE)       & 192                      & CBC    \\
+\hline \texttt{3des-ctr}                      & 3DES (EDE)       & 192                      & CTR    \\
+\hline \texttt{serpent128-cbc}                & Serpent          & 128                      & CBC    \\
+\hline \texttt{serpent128-ctr}                & Serpent          & 128                      & CTR    \\
+\hline \texttt{serpent192-cbc}                & Serpent          & 192                      & CBC    \\
+\hline \texttt{serpent192-ctr}                & Serpent          & 192                      & CTR    \\
+\hline \texttt{serpent256-cbc}                & Serpent          & 256                      & CBC    \\
+\hline \texttt{serpent256-ctr}                & Serpent          & 256                      & CTR    \\
+\hline \texttt{twofish128-cbc}                & Twofish          & 128                      & CBC    \\
+\hline \texttt{twofish128-ctr}                & Twofish          & 128                      & CTR    \\
+\hline \texttt{twofish192-cbc}                & Twofish          & 192                      & CBC    \\
+\hline \texttt{twofish192-ctr}                & Twofish          & 192                      & CTR    \\
+\hline \texttt{twofish-cbc}                   & Twofish          & 256                      & CBC    \\
+\hline \texttt{twofish256-cbc}                & Twofish          & 256                      & CBC    \\
+\hline \texttt{twofish256-ctr}                & Twofish          & 256                      & CTR    \\
 \hline
 \end{tabular}
 \end{small}

+ 54 - 0
notes/cipher_tv.txt

@@ -1433,6 +1433,60 @@ Key Size: 8 bytes
 49: 6B901B2B79B6950C
 
 
+Cipher: desx
+Key Size: 24 bytes
+ 0: F490BAC08301C6C9
+ 1: 5668E3676102907F
+ 2: 5BA2BFCC35AED470
+ 3: B2CC7DBA467E62C6
+ 4: B4BF359A876FAC3E
+ 5: 2EF94EA88C1ACE79
+ 6: 9C175106D3484502
+ 7: 38F466A89DFA587F
+ 8: 745F3EB5BEDE929C
+ 9: 9EBA1D104A86E113
+10: 561DC144F7A2CB5F
+11: E1EBF96BD996F292
+12: 4D96B8CD7D26DA9C
+13: DA59711131B18AF3
+14: F5EEC897F79D3597
+15: 3A39A7F0060373CB
+16: DA95839AA553147F
+17: 8A0BBA6804BDFFF2
+18: B0A0881F389062B5
+19: C6878531FF4888EC
+20: 9C73653BDB9EBFFD
+21: EF81557E5B539A4C
+22: 959ADE9663CC395D
+23: D22C6460C0580E1B
+24: E3B7EFC7DC2EEB28
+25: ACF66715DFE81D84
+26: CD2FC182D9A0F565
+27: 34F6DED980E437FA
+28: 313DE7369F9D1BB9
+29: 554A743622A42A3D
+30: 6F460E480078F091
+31: E752181D34A8FED2
+32: E0C7F0F53F84830B
+33: 1159C652EB6460E1
+34: 2A68847D986CBF7D
+35: B3CB050C29C86EFC
+36: 2C0EB50DBCA918EA
+37: CA1D0D17D185D9BE
+38: 3CB9EB47E1E05CC1
+39: 2AA3DE0A38F3A0F5
+40: EA43C7125932D2A7
+41: 79A23C0EA9E6C11E
+42: B711BFC1DE05D9B0
+43: BCF5ADD7751EED39
+44: 7A41A2FE64720CA2
+45: DC43A35EB0489FE9
+46: 9353A9FBD060B991
+47: 19DD74A5D948AC15
+48: F0E3B7B2D6E328F5
+49: 77C25E387D80E071
+
+
 Cipher: 3des
 Key Size: 16 bytes
  0: DF0B6C9C31CD0CE4

+ 19 - 0
notes/eax_tv.txt

@@ -313,6 +313,25 @@ EAX-des (8 byte key)
  15: 8D1A1E888BBB8648E638C4E74E11B8, 685E006C441448B8
  16: 93AE906B8BE4EAC8ED6D8F48F04A7AFF, 71DD7AF752FE28FB
 
+EAX-desx (24 byte key)
+  0: , FA66F07E473109A2
+  1: 4B, 576970495248BB09
+  2: D81C, 3AE9E470ABFBEEB1
+  3: 5B2442, F6B3BDC55CBD01A2
+  4: B1A495A8, AAD2D78BC0525DA2
+  5: 7723413A8D, 51D3134CBC32AD9B
+  6: 6F9D40815E10, 552C146A7A769E9E
+  7: B3292E406C9B92, D3ACE79B2D69877B
+  8: EF7513D71D52C33A, 64935E1AE8C416B1
+  9: 068AEDE3E0E1B0DC11, 2C5698925FFC70BF
+ 10: 76A5664A3D5DF553BAC4, 52B9D560C0D9BB0D
+ 11: CC6128B6BD0035354CF3A3, A8BA535862B221BD
+ 12: CB499A58CF55D016B79192EF, 76842391A45C6674
+ 13: 4903FBC696A256D4AC16A3EFD2, 39978842103FE097
+ 14: 9CD5671BEDF4EB8D519A72310A37, F00809AA017E81C5
+ 15: 9F62AB705A285EBF998FCF401166BA, 506244F55C4EAD84
+ 16: 110161EEF9B3CE543DB12EA8682866D4, 7A12BE4371963521
+
 EAX-3des (24 byte key)
   0: , 8914311BB990B725
   1: D8, 2094EDC5D03E54B1

+ 19 - 0
notes/ocb_tv.txt

@@ -313,6 +313,25 @@ OCB-des (8 byte key)
  15: FB3BCC650B29F418930A467EA4FB73, 64D12723E100F08B
  16: DE1C27E9B3C391AF5DF403291F2C084A, 6BADE4638AE46BE2
 
+OCB-desx (24 byte key)
+  0: , 972B4CC480AEA6A9
+  1: CB, C46CC58DE9615963
+  2: 2911, 9B5117BF9530018F
+  3: 844501, 308F0F36D3313B67
+  4: 0C8CB549, 3F72789FB54CC9B1
+  5: 581FA34114, 1B86E66203EBF9EE
+  6: D0BBE3E43961, 59F730D5ABF13265
+  7: 046529AB0EDD17, 240FF6134AA5327B
+  8: FF4F32C3A96D61D9, 5DE9B81CC39ACC61
+  9: E94A99D609BE5B1A6D, 443F4948DE64E6A0
+ 10: B3E783B59853EE1EBD36, F04B41EAAB9CDE18
+ 11: 0BB36CE35BB8050169F6F2, 598A0705C800BC04
+ 12: BE946B1CB03E7E5DA1CC12B8, 288B827CEA810662
+ 13: 3FEC137C657FF1F2B34F4C5E56, F9248F59D1033253
+ 14: 626DC4527055E80E68A6A1FE0F78, D8AA67D5ABD0B6A5
+ 15: 476247537A509BC42BCD6DEC7F9506, 2C2D0385066B4815
+ 16: 5D32BFE0B9ACB62B6AC29D43A0535A25, DE247F5F809C6CEC
+
 OCB-3des (24 byte key)
   0: , 9CB7074F93CD37DD
   1: 4D, 51541A838A154E0B

+ 19 - 0
notes/omac_tv.txt

@@ -313,6 +313,25 @@ OMAC-des (8 byte key)
  15: 7FB7CE0862958B37
  16: 55097816B10C549B
 
+OMAC-desx (24 byte key)
+  0: 3DCD366D8E4D6EB8
+  1: AD1DF426B344F922
+  2: C50DD51B953E37EF
+  3: F732DD355496D72F
+  4: B8A9D3819024AD6E
+  5: 7C4624125A33C0DF
+  6: 378D83372E82296F
+  7: 974C7613AF191E95
+  8: C856C3F7B1A944FA
+  9: F7898CFD34AFECE2
+ 10: 9B5B09251EB0F44B
+ 11: 369D0AC2E71641E8
+ 12: B798AFD13EDFD831
+ 13: 066207F46EA9B6F2
+ 14: 4083189F5CE42C5F
+ 15: 6D04E4B9E2ECA8DB
+ 16: 9D9EE9A8B5AC27C2
+
 OMAC-3des (24 byte key)
   0: 7F07A9EA8ECEDF9E
   1: 4E2A652EB5FBF5F8

+ 19 - 0
notes/pmac_tv.txt

@@ -313,6 +313,25 @@ PMAC-des (8 byte key)
  15: FCE22E6CAD528B49
  16: 993884FB9B3FB620
 
+PMAC-desx (24 byte key)
+  0: CFC3AC7F6B9BC6C4
+  1: 15F83A2E582CA5DB
+  2: 648B0A54C2A44D96
+  3: 76BD5FCA60D3E0D8
+  4: 59E994CED4C82509
+  5: 15B6B80165023A25
+  6: 095AEDA02E235237
+  7: FDEEF329DD64EE7D
+  8: 1FE317FD5338ADEF
+  9: 0E0530FC5984E574
+ 10: 7D670A434BDF5E6E
+ 11: AD0C2D07F449969F
+ 12: 023D921C2523A41D
+ 13: E98F2BE666A5749D
+ 14: FA9DB0029446CA31
+ 15: AF3350DCF2A5D6AC
+ 16: E72861AE67EF88E2
+
 PMAC-3des (24 byte key)
   0: E42CCBC9C9457DF6
   1: FE766F7930557708

+ 156 - 7
src/ciphers/des.c

@@ -40,6 +40,20 @@ const struct ltc_cipher_descriptor des3_desc =
     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
+const struct ltc_cipher_descriptor desx_desc =
+{
+    "desx",
+    27,
+    24, 24, 8, 16,
+    &desx_setup,
+    &desx_ecb_encrypt,
+    &desx_ecb_decrypt,
+    &desx_test,
+    &desx_done,
+    &desx_keysize,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
 static const ulong32 bytebit[8] =
 {
     0200, 0100, 040, 020, 010, 04, 02, 01
@@ -1511,7 +1525,7 @@ static void desfunc(ulong32 *block, const ulong32 *keys)
 #endif
 
  /**
-    Initialize the LTC_DES block cipher
+    Initialize the DES block cipher
     @param key The symmetric key you wish to pass
     @param keylen The key length in bytes
     @param num_rounds The number of rounds desired (0 for default)
@@ -1538,7 +1552,36 @@ int des_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_ke
 }
 
  /**
-    Initialize the 3LTC_DES-EDE block cipher
+    Initialize the DES-X block cipher
+    @param key The symmetric key you wish to pass
+    @param keylen The key length in bytes
+    @param num_rounds The number of rounds desired (0 for default)
+    @param skey The key in as scheduled by this function.
+    @return CRYPT_OK if successful
+ */
+int desx_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey)
+{
+   if(num_rounds != 0 && num_rounds != 16) {
+       return CRYPT_INVALID_ROUNDS;
+   }
+
+   if (keylen != 24) {
+       return CRYPT_INVALID_KEYSIZE;
+   }
+
+   deskey(key, EN0, skey->desx.ek);
+   deskey(key, DE1, skey->desx.dk);
+
+   LOAD32H(skey->desx.k[0][0], key + 8);
+   LOAD32H(skey->desx.k[0][1], key + 12);
+   LOAD32H(skey->desx.k[1][0], key + 16);
+   LOAD32H(skey->desx.k[1][1], key + 20);
+
+   return CRYPT_OK;
+}
+
+ /**
+    Initialize the 3DES-EDE block cipher
     @param key The symmetric key you wish to pass
     @param keylen The key length in bytes
     @param num_rounds The number of rounds desired (0 for default)
@@ -1580,7 +1623,7 @@ int des3_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_k
 }
 
 /**
-  Encrypts a block of text with LTC_DES
+  Encrypts a block of text with DES
   @param pt The input plaintext (8 bytes)
   @param ct The output ciphertext (8 bytes)
   @param skey The key as scheduled
@@ -1601,7 +1644,7 @@ int des_ecb_encrypt(const unsigned char *pt, unsigned char *ct, const symmetric_
 }
 
 /**
-  Decrypts a block of text with LTC_DES
+  Decrypts a block of text with DES
   @param ct The input ciphertext (8 bytes)
   @param pt The output plaintext (8 bytes)
   @param skey The key as scheduled
@@ -1622,7 +1665,57 @@ int des_ecb_decrypt(const unsigned char *ct, unsigned char *pt, const symmetric_
 }
 
 /**
-  Encrypts a block of text with 3LTC_DES-EDE
+  Encrypts a block of text with DES-X
+  @param pt The input plaintext (8 bytes)
+  @param ct The output ciphertext (8 bytes)
+  @param skey The key as scheduled
+  @return CRYPT_OK if successful
+*/
+int desx_ecb_encrypt(const unsigned char *pt, unsigned char *ct, const symmetric_key *skey)
+{
+    ulong32 work[2];
+    LTC_ARGCHK(pt   != NULL);
+    LTC_ARGCHK(ct   != NULL);
+    LTC_ARGCHK(skey != NULL);
+    LOAD32H(work[0], pt+0);
+    LOAD32H(work[1], pt+4);
+    work[0] ^= skey->desx.k[0][0];
+    work[1] ^= skey->desx.k[0][1];
+    desfunc(work, skey->desx.ek);
+    work[0] ^= skey->desx.k[1][0];
+    work[1] ^= skey->desx.k[1][1];
+    STORE32H(work[0],ct+0);
+    STORE32H(work[1],ct+4);
+    return CRYPT_OK;
+}
+
+/**
+  Decrypts a block of text with DES-X
+  @param ct The input ciphertext (8 bytes)
+  @param pt The output plaintext (8 bytes)
+  @param skey The key as scheduled
+  @return CRYPT_OK if successful
+*/
+int desx_ecb_decrypt(const unsigned char *ct, unsigned char *pt, const symmetric_key *skey)
+{
+    ulong32 work[2];
+    LTC_ARGCHK(pt   != NULL);
+    LTC_ARGCHK(ct   != NULL);
+    LTC_ARGCHK(skey != NULL);
+    LOAD32H(work[0], ct+0);
+    LOAD32H(work[1], ct+4);
+    work[0] ^= skey->desx.k[1][0];
+    work[1] ^= skey->desx.k[1][1];
+    desfunc(work, skey->des.dk);
+    work[0] ^= skey->desx.k[0][0];
+    work[1] ^= skey->desx.k[0][1];
+    STORE32H(work[0],pt+0);
+    STORE32H(work[1],pt+4);
+    return CRYPT_OK;
+}
+
+/**
+  Encrypts a block of text with 3DES-EDE
   @param pt The input plaintext (8 bytes)
   @param ct The output ciphertext (8 bytes)
   @param skey The key as scheduled
@@ -1646,7 +1739,7 @@ int des3_ecb_encrypt(const unsigned char *pt, unsigned char *ct, const symmetric
 }
 
 /**
-  Decrypts a block of text with 3LTC_DES-EDE
+  Decrypts a block of text with 3DES-EDE
   @param ct The input ciphertext (8 bytes)
   @param pt The output plaintext (8 bytes)
   @param skey The key as scheduled
@@ -1669,7 +1762,7 @@ int des3_ecb_decrypt(const unsigned char *ct, unsigned char *pt, const symmetric
 }
 
 /**
-  Performs a self-test of the LTC_DES block cipher
+  Performs a self-test of the DES block cipher
   @return CRYPT_OK if functional, CRYPT_NOP if self-test has been disabled
 */
 int des_test(void)
@@ -1964,6 +2057,39 @@ int des_test(void)
   #endif
 }
 
+int desx_test(void)
+{
+ #ifndef LTC_TEST
+    return CRYPT_NOP;
+ #else
+    unsigned char key[24], pt[8], tmp[8];
+    symmetric_key skey;
+    int i, err;
+
+    if ((err = des_test()) != CRYPT_OK) {
+        return err;
+    }
+
+    /* See if we can encrypt all zero bytes 1000 times, decrypt and come back to where we started */
+
+    for (i = 0; i < 24; i++) key[i] = i;
+
+    if ((err = desx_setup(key, 24, 0, &skey)) != CRYPT_OK) {
+        return err;
+    }
+
+    for (i = 0; i < 8; i++) pt[i] = tmp[i] = 0;
+    for (i = 0; i < 1000; i++) desx_ecb_encrypt(tmp, tmp, &skey);
+    for (i = 0; i < 1000; i++) desx_ecb_decrypt(tmp, tmp, &skey);
+
+    if (compare_testvector(tmp, 8, pt, 8, "DES-X", 0) != 0) {
+        return CRYPT_FAIL_TESTVECTOR;
+    }
+
+    return CRYPT_OK;
+ #endif
+}
+
 int des3_test(void)
 {
  #ifndef LTC_TEST
@@ -2046,6 +2172,14 @@ void des_done(symmetric_key *skey)
   LTC_UNUSED_PARAM(skey);
 }
 
+/** Terminate the context
+   @param skey    The scheduled key
+*/
+void desx_done(symmetric_key *skey)
+{
+  LTC_UNUSED_PARAM(skey);
+}
+
 /** Terminate the context
    @param skey    The scheduled key
 */
@@ -2070,6 +2204,21 @@ int des_keysize(int *keysize)
     return CRYPT_OK;
 }
 
+/**
+  Gets suitable key size
+  @param keysize [in/out] The length of the recommended key (in bytes).  This function will store the suitable size back in this variable.
+  @return CRYPT_OK if the input key size is acceptable.
+*/
+int desx_keysize(int *keysize)
+{
+    LTC_ARGCHK(keysize != NULL);
+    if(*keysize < 24) {
+        return CRYPT_INVALID_KEYSIZE;
+    }
+    *keysize = 24;
+    return CRYPT_OK;
+}
+
 /**
   Gets suitable key size
   @param keysize [in/out] The length of the recommended key (in bytes).  This function will store the suitable size back in this variable.

+ 8 - 6
src/encauth/chachapoly/chacha20poly1305_done.c

@@ -21,13 +21,15 @@ int chacha20poly1305_done(chacha20poly1305_state *st, unsigned char *tag, unsign
 
    LTC_ARGCHK(st != NULL);
 
-   padlen = 16 - (unsigned long)(st->ctlen % 16);
-   if (padlen < 16) {
-     if ((err = poly1305_process(&st->poly, padzero, padlen)) != CRYPT_OK) return err;
+   if (!st->openssh_compat) {
+      padlen = 16 - (unsigned long)(st->ctlen % 16);
+      if (padlen < 16) {
+        if ((err = poly1305_process(&st->poly, padzero, padlen)) != CRYPT_OK) return err;
+      }
+      STORE64L(st->aadlen, buf);
+      STORE64L(st->ctlen, buf + 8);
+      if ((err = poly1305_process(&st->poly, buf, 16)) != CRYPT_OK)           return err;
    }
-   STORE64L(st->aadlen, buf);
-   STORE64L(st->ctlen, buf + 8);
-   if ((err = poly1305_process(&st->poly, buf, 16)) != CRYPT_OK)           return err;
    if ((err = poly1305_done(&st->poly, tag, taglen)) != CRYPT_OK)          return err;
    if ((err = chacha_done(&st->chacha)) != CRYPT_OK)                       return err;
    return CRYPT_OK;

+ 1 - 0
src/encauth/chachapoly/chacha20poly1305_init.c

@@ -14,6 +14,7 @@
 */
 int chacha20poly1305_init(chacha20poly1305_state *st, const unsigned char *key, unsigned long keylen)
 {
+   XMEMSET(st, 0, sizeof(*st));
    return chacha_setup(&st->chacha, key, keylen, 20);
 }
 

+ 4 - 0
src/encauth/chachapoly/chacha20poly1305_memory.c

@@ -40,6 +40,10 @@ int chacha20poly1305_memory(const unsigned char *key, unsigned long keylen,
    LTC_ARGCHK(taglen != NULL);
 
    if ((err = chacha20poly1305_init(&st, key, keylen)) != CRYPT_OK)          { goto LBL_ERR; }
+
+   st.openssh_compat = (direction & CHACHA20POLY1305_OPENSSH_COMPAT) ? 1 : 0;
+   direction &= ~(CHACHA20POLY1305_OPENSSH_COMPAT);
+
    if ((err = chacha20poly1305_setiv(&st, iv, ivlen)) != CRYPT_OK)           { goto LBL_ERR; }
    if (aad && aadlen > 0) {
       if ((err = chacha20poly1305_add_aad(&st, aad, aadlen)) != CRYPT_OK)    { goto LBL_ERR; }

+ 13 - 1
src/headers/tomcrypt_cipher.h

@@ -97,6 +97,11 @@ struct des_key {
     ulong32 ek[32], dk[32];
 };
 
+struct desx_key {
+    ulong32 ek[32], dk[32];
+    ulong32 k[2][2];
+};
+
 struct des3_key {
     ulong32 ek[3][32], dk[3][32];
 };
@@ -176,6 +181,7 @@ struct tea_key {
 typedef union Symmetric_key {
 #ifdef LTC_DES
    struct des_key des;
+   struct desx_key desx;
    struct des3_key des3;
 #endif
 #ifdef LTC_RC2
@@ -757,13 +763,19 @@ int des_ecb_decrypt(const unsigned char *ct, unsigned char *pt, const symmetric_
 int des_test(void);
 void des_done(symmetric_key *skey);
 int des_keysize(int *keysize);
+int desx_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
+int desx_ecb_encrypt(const unsigned char *pt, unsigned char *ct, const symmetric_key *skey);
+int desx_ecb_decrypt(const unsigned char *ct, unsigned char *pt, const symmetric_key *skey);
+int desx_test(void);
+void desx_done(symmetric_key *skey);
+int desx_keysize(int *keysize);
 int des3_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey);
 int des3_ecb_encrypt(const unsigned char *pt, unsigned char *ct, const symmetric_key *skey);
 int des3_ecb_decrypt(const unsigned char *ct, unsigned char *pt, const symmetric_key *skey);
 int des3_test(void);
 void des3_done(symmetric_key *skey);
 int des3_keysize(int *keysize);
-extern const struct ltc_cipher_descriptor des_desc, des3_desc;
+extern const struct ltc_cipher_descriptor des_desc, desx_desc, des3_desc;
 #endif
 
 #ifdef LTC_CAST5

+ 4 - 3
src/headers/tomcrypt_mac.h

@@ -541,11 +541,12 @@ typedef struct {
    chacha_state chacha;
    ulong64 aadlen;
    ulong64 ctlen;
-   int aadflg;
+   int aadflg, openssh_compat;
 } chacha20poly1305_state;
 
-#define CHACHA20POLY1305_ENCRYPT LTC_ENCRYPT
-#define CHACHA20POLY1305_DECRYPT LTC_DECRYPT
+#define CHACHA20POLY1305_ENCRYPT          LTC_ENCRYPT
+#define CHACHA20POLY1305_DECRYPT          LTC_DECRYPT
+#define CHACHA20POLY1305_OPENSSH_COMPAT   2
 
 int chacha20poly1305_init(chacha20poly1305_state *st, const unsigned char *key, unsigned long keylen);
 int chacha20poly1305_setiv(chacha20poly1305_state *st, const unsigned char *iv, unsigned long ivlen);

+ 14 - 1
src/headers/tomcrypt_private.h

@@ -263,7 +263,19 @@ int base64_encode_pem(const unsigned char *in,  unsigned long inlen,
 
 #ifdef LTC_PEM
 enum cipher_mode {
-   cm_none, cm_cbc, cm_cfb, cm_ctr, cm_ofb, cm_stream, cm_gcm
+   cm_modes =           0x00ff,
+   cm_flags =           0xff00,
+   /* Flags */
+   cm_openssh =         0x0100,
+   /* Modes */
+   cm_none =            0x0000,
+   cm_cbc =             0x0001,
+   cm_cfb =             0x0002,
+   cm_ctr =             0x0003,
+   cm_ofb =             0x0004,
+   cm_stream =          0x0005,
+   cm_gcm =             0x0006,
+   cm_stream_openssh =  cm_stream | cm_openssh,
 };
 
 struct blockcipher_info {
@@ -350,6 +362,7 @@ int pbes2_extract(const ltc_asn1_list *s, pbes_arg *res);
 int pem_decrypt(unsigned char *data, unsigned long *datalen,
                 unsigned char *key,  unsigned long keylen,
                 unsigned char *iv,   unsigned long ivlen,
+                unsigned char *tag,  unsigned long taglen,
                 const struct blockcipher_info *info,
                 enum padding_type padding);
 #ifndef LTC_NO_FILE

+ 2 - 0
src/misc/crypt/crypt.c

@@ -84,6 +84,8 @@ const char *crypt_build_settings =
 #endif
 #if defined(LTC_DES)
    "   DES\n"
+   "   DES-X\n"
+   "   3DES\n"
 #endif
 #if defined(LTC_CAST5)
    "   CAST5\n"

+ 1 - 0
src/misc/crypt/crypt_register_all_ciphers.c

@@ -58,6 +58,7 @@ int register_all_ciphers(void)
 #endif
 #ifdef LTC_DES
    REGISTER_CIPHER(&des_desc);
+   REGISTER_CIPHER(&desx_desc);
    REGISTER_CIPHER(&des3_desc);
 #endif
 #ifdef LTC_CAST5

+ 107 - 54
src/misc/pem/pem.c

@@ -75,66 +75,72 @@ const struct str pem_ssh_comment = { SET_CSTR(, "Comment: ") };
 const struct str pem_dek_info_start = { SET_CSTR(, "DEK-Info: ") };
 const struct blockcipher_info pem_dek_infos[] =
    {
-      { .name = "AES-128-CBC,",      .algo = "aes",      .keylen = 128 / 8, .mode = cm_cbc, },
-      { .name = "AES-192-CBC,",      .algo = "aes",      .keylen = 192 / 8, .mode = cm_cbc, },
-      { .name = "AES-256-CBC,",      .algo = "aes",      .keylen = 256 / 8, .mode = cm_cbc, },
-      { .name = "AES-128-CFB,",      .algo = "aes",      .keylen = 128 / 8, .mode = cm_cfb, },
-      { .name = "AES-192-CFB,",      .algo = "aes",      .keylen = 192 / 8, .mode = cm_cfb, },
-      { .name = "AES-256-CFB,",      .algo = "aes",      .keylen = 256 / 8, .mode = cm_cfb, },
-      { .name = "AES-128-CTR,",      .algo = "aes",      .keylen = 128 / 8, .mode = cm_ctr, },
-      { .name = "AES-192-CTR,",      .algo = "aes",      .keylen = 192 / 8, .mode = cm_ctr, },
-      { .name = "AES-256-CTR,",      .algo = "aes",      .keylen = 256 / 8, .mode = cm_ctr, },
-      { .name = "AES-128-OFB,",      .algo = "aes",      .keylen = 128 / 8, .mode = cm_ofb, },
-      { .name = "AES-192-OFB,",      .algo = "aes",      .keylen = 192 / 8, .mode = cm_ofb, },
-      { .name = "AES-256-OFB,",      .algo = "aes",      .keylen = 256 / 8, .mode = cm_ofb, },
-      { .name = "BF-CBC,",           .algo = "blowfish", .keylen = 128 / 8, .mode = cm_cbc, },
-      { .name = "BF-CFB,",           .algo = "blowfish", .keylen = 128 / 8, .mode = cm_cfb, },
-      { .name = "BF-OFB,",           .algo = "blowfish", .keylen = 128 / 8, .mode = cm_ofb, },
-      { .name = "CAMELLIA-128-CBC,", .algo = "camellia", .keylen = 128 / 8, .mode = cm_cbc, },
-      { .name = "CAMELLIA-192-CBC,", .algo = "camellia", .keylen = 192 / 8, .mode = cm_cbc, },
-      { .name = "CAMELLIA-256-CBC,", .algo = "camellia", .keylen = 256 / 8, .mode = cm_cbc, },
-      { .name = "CAMELLIA-128-CFB,", .algo = "camellia", .keylen = 128 / 8, .mode = cm_cfb, },
-      { .name = "CAMELLIA-192-CFB,", .algo = "camellia", .keylen = 192 / 8, .mode = cm_cfb, },
-      { .name = "CAMELLIA-256-CFB,", .algo = "camellia", .keylen = 256 / 8, .mode = cm_cfb, },
-      { .name = "CAMELLIA-128-CTR,", .algo = "camellia", .keylen = 128 / 8, .mode = cm_ctr, },
-      { .name = "CAMELLIA-192-CTR,", .algo = "camellia", .keylen = 192 / 8, .mode = cm_ctr, },
-      { .name = "CAMELLIA-256-CTR,", .algo = "camellia", .keylen = 256 / 8, .mode = cm_ctr, },
-      { .name = "CAMELLIA-128-OFB,", .algo = "camellia", .keylen = 128 / 8, .mode = cm_ofb, },
-      { .name = "CAMELLIA-192-OFB,", .algo = "camellia", .keylen = 192 / 8, .mode = cm_ofb, },
-      { .name = "CAMELLIA-256-OFB,", .algo = "camellia", .keylen = 256 / 8, .mode = cm_ofb, },
-      { .name = "CAST5-CBC,",        .algo = "cast5",    .keylen = 128 / 8, .mode = cm_cbc, },
-      { .name = "CAST5-CFB,",        .algo = "cast5",    .keylen = 128 / 8, .mode = cm_cfb, },
-      { .name = "CAST5-OFB,",        .algo = "cast5",    .keylen = 128 / 8, .mode = cm_ofb, },
-      { .name = "DES-EDE3-CBC,",     .algo = "3des",     .keylen = 192 / 8, .mode = cm_cbc, },
-      { .name = "DES-EDE3-CFB,",     .algo = "3des",     .keylen = 192 / 8, .mode = cm_cfb, },
-      { .name = "DES-EDE3-OFB,",     .algo = "3des",     .keylen = 192 / 8, .mode = cm_ofb, },
-      { .name = "DES-CBC,",          .algo = "des",      .keylen =  64 / 8, .mode = cm_cbc, },
-      { .name = "DES-CFB,",          .algo = "des",      .keylen =  64 / 8, .mode = cm_cfb, },
-      { .name = "DES-OFB,",          .algo = "des",      .keylen =  64 / 8, .mode = cm_ofb, },
-      { .name = "IDEA-CBC,",         .algo = "idea",     .keylen = 128 / 8, .mode = cm_cbc, },
-      { .name = "IDEA-CFB,",         .algo = "idea",     .keylen = 128 / 8, .mode = cm_cfb, },
-      { .name = "IDEA-OFB,",         .algo = "idea",     .keylen = 128 / 8, .mode = cm_ofb, },
-      { .name = "RC5-CBC,",          .algo = "rc5",      .keylen = 128 / 8, .mode = cm_cbc, },
-      { .name = "RC5-CFB,",          .algo = "rc5",      .keylen = 128 / 8, .mode = cm_cfb, },
-      { .name = "RC5-OFB,",          .algo = "rc5",      .keylen = 128 / 8, .mode = cm_ofb, },
-      { .name = "RC2-40-CBC,",       .algo = "rc2",      .keylen =  40 / 8, .mode = cm_cbc, },
-      { .name = "RC2-64-CBC,",       .algo = "rc2",      .keylen =  64 / 8, .mode = cm_cbc, },
-      { .name = "RC2-CBC,",          .algo = "rc2",      .keylen = 128 / 8, .mode = cm_cbc, },
-      { .name = "RC2-CFB,",          .algo = "rc2",      .keylen = 128 / 8, .mode = cm_cfb, },
-      { .name = "RC2-OFB,",          .algo = "rc2",      .keylen = 128 / 8, .mode = cm_ofb, },
-      { .name = "SEED-CBC,",         .algo = "seed",     .keylen = 128 / 8, .mode = cm_cbc, },
-      { .name = "SEED-CFB,",         .algo = "seed",     .keylen = 128 / 8, .mode = cm_cfb, },
-      { .name = "SEED-OFB,",         .algo = "seed",     .keylen = 128 / 8, .mode = cm_ofb, },
+      { .name = "AES-128-CBC,",      .algo = "aes",      .keylen = 128 / 8, .mode = cm_cbc,    },
+      { .name = "AES-192-CBC,",      .algo = "aes",      .keylen = 192 / 8, .mode = cm_cbc,    },
+      { .name = "AES-256-CBC,",      .algo = "aes",      .keylen = 256 / 8, .mode = cm_cbc,    },
+      { .name = "AES-128-CFB,",      .algo = "aes",      .keylen = 128 / 8, .mode = cm_cfb,    },
+      { .name = "AES-192-CFB,",      .algo = "aes",      .keylen = 192 / 8, .mode = cm_cfb,    },
+      { .name = "AES-256-CFB,",      .algo = "aes",      .keylen = 256 / 8, .mode = cm_cfb,    },
+      { .name = "AES-128-CTR,",      .algo = "aes",      .keylen = 128 / 8, .mode = cm_ctr,    },
+      { .name = "AES-192-CTR,",      .algo = "aes",      .keylen = 192 / 8, .mode = cm_ctr,    },
+      { .name = "AES-256-CTR,",      .algo = "aes",      .keylen = 256 / 8, .mode = cm_ctr,    },
+      { .name = "AES-128-OFB,",      .algo = "aes",      .keylen = 128 / 8, .mode = cm_ofb,    },
+      { .name = "AES-192-OFB,",      .algo = "aes",      .keylen = 192 / 8, .mode = cm_ofb,    },
+      { .name = "AES-256-OFB,",      .algo = "aes",      .keylen = 256 / 8, .mode = cm_ofb,    },
+      { .name = "BF-CBC,",           .algo = "blowfish", .keylen = 128 / 8, .mode = cm_cbc,    },
+      { .name = "BF-CFB,",           .algo = "blowfish", .keylen = 128 / 8, .mode = cm_cfb,    },
+      { .name = "BF-OFB,",           .algo = "blowfish", .keylen = 128 / 8, .mode = cm_ofb,    },
+      { .name = "CAMELLIA-128-CBC,", .algo = "camellia", .keylen = 128 / 8, .mode = cm_cbc,    },
+      { .name = "CAMELLIA-192-CBC,", .algo = "camellia", .keylen = 192 / 8, .mode = cm_cbc,    },
+      { .name = "CAMELLIA-256-CBC,", .algo = "camellia", .keylen = 256 / 8, .mode = cm_cbc,    },
+      { .name = "CAMELLIA-128-CFB,", .algo = "camellia", .keylen = 128 / 8, .mode = cm_cfb,    },
+      { .name = "CAMELLIA-192-CFB,", .algo = "camellia", .keylen = 192 / 8, .mode = cm_cfb,    },
+      { .name = "CAMELLIA-256-CFB,", .algo = "camellia", .keylen = 256 / 8, .mode = cm_cfb,    },
+      { .name = "CAMELLIA-128-CTR,", .algo = "camellia", .keylen = 128 / 8, .mode = cm_ctr,    },
+      { .name = "CAMELLIA-192-CTR,", .algo = "camellia", .keylen = 192 / 8, .mode = cm_ctr,    },
+      { .name = "CAMELLIA-256-CTR,", .algo = "camellia", .keylen = 256 / 8, .mode = cm_ctr,    },
+      { .name = "CAMELLIA-128-OFB,", .algo = "camellia", .keylen = 128 / 8, .mode = cm_ofb,    },
+      { .name = "CAMELLIA-192-OFB,", .algo = "camellia", .keylen = 192 / 8, .mode = cm_ofb,    },
+      { .name = "CAMELLIA-256-OFB,", .algo = "camellia", .keylen = 256 / 8, .mode = cm_ofb,    },
+      { .name = "CAST5-CBC,",        .algo = "cast5",    .keylen = 128 / 8, .mode = cm_cbc,    },
+      { .name = "CAST5-CFB,",        .algo = "cast5",    .keylen = 128 / 8, .mode = cm_cfb,    },
+      { .name = "CAST5-OFB,",        .algo = "cast5",    .keylen = 128 / 8, .mode = cm_ofb,    },
+      { .name = "ChaCha20,",         .algo = "chacha20", .keylen = 256 / 8, .mode = cm_stream, },
+      { .name = "DES-EDE-CBC,",      .algo = "3des",     .keylen = 128 / 8, .mode = cm_cbc,    },
+      { .name = "DES-EDE-CFB,",      .algo = "3des",     .keylen = 128 / 8, .mode = cm_cfb,    },
+      { .name = "DES-EDE-OFB,",      .algo = "3des",     .keylen = 128 / 8, .mode = cm_ofb,    },
+      { .name = "DES-EDE3-CBC,",     .algo = "3des",     .keylen = 192 / 8, .mode = cm_cbc,    },
+      { .name = "DES-EDE3-CFB,",     .algo = "3des",     .keylen = 192 / 8, .mode = cm_cfb,    },
+      { .name = "DES-EDE3-OFB,",     .algo = "3des",     .keylen = 192 / 8, .mode = cm_ofb,    },
+      { .name = "DES-CBC,",          .algo = "des",      .keylen =  64 / 8, .mode = cm_cbc,    },
+      { .name = "DES-CFB,",          .algo = "des",      .keylen =  64 / 8, .mode = cm_cfb,    },
+      { .name = "DES-OFB,",          .algo = "des",      .keylen =  64 / 8, .mode = cm_ofb,    },
+      { .name = "DESX-CBC,",         .algo = "desx",     .keylen = 192 / 8, .mode = cm_cbc,    },
+      { .name = "IDEA-CBC,",         .algo = "idea",     .keylen = 128 / 8, .mode = cm_cbc,    },
+      { .name = "IDEA-CFB,",         .algo = "idea",     .keylen = 128 / 8, .mode = cm_cfb,    },
+      { .name = "IDEA-OFB,",         .algo = "idea",     .keylen = 128 / 8, .mode = cm_ofb,    },
+      { .name = "RC5-CBC,",          .algo = "rc5",      .keylen = 128 / 8, .mode = cm_cbc,    },
+      { .name = "RC5-CFB,",          .algo = "rc5",      .keylen = 128 / 8, .mode = cm_cfb,    },
+      { .name = "RC5-OFB,",          .algo = "rc5",      .keylen = 128 / 8, .mode = cm_ofb,    },
+      { .name = "RC2-40-CBC,",       .algo = "rc2",      .keylen =  40 / 8, .mode = cm_cbc,    },
+      { .name = "RC2-64-CBC,",       .algo = "rc2",      .keylen =  64 / 8, .mode = cm_cbc,    },
+      { .name = "RC2-CBC,",          .algo = "rc2",      .keylen = 128 / 8, .mode = cm_cbc,    },
+      { .name = "RC2-CFB,",          .algo = "rc2",      .keylen = 128 / 8, .mode = cm_cfb,    },
+      { .name = "RC2-OFB,",          .algo = "rc2",      .keylen = 128 / 8, .mode = cm_ofb,    },
+      { .name = "SEED-CBC,",         .algo = "seed",     .keylen = 128 / 8, .mode = cm_cbc,    },
+      { .name = "SEED-CFB,",         .algo = "seed",     .keylen = 128 / 8, .mode = cm_cfb,    },
+      { .name = "SEED-OFB,",         .algo = "seed",     .keylen = 128 / 8, .mode = cm_ofb,    },
    };
 const unsigned long pem_dek_infos_num = sizeof(pem_dek_infos)/sizeof(pem_dek_infos[0]);
 
 int pem_decrypt(unsigned char *data, unsigned long *datalen,
                 unsigned char *key,  unsigned long keylen,
                 unsigned char *iv,   unsigned long ivlen,
+                unsigned char *tag,  unsigned long taglen,
                 const struct blockcipher_info *info,
                 enum padding_type padding)
 {
-   int err, cipher;
+   int err, cipher = -1;
    struct {
       union {
 #ifdef LTC_CBC_MODE
@@ -151,10 +157,13 @@ int pem_decrypt(unsigned char *data, unsigned long *datalen,
 #endif
       } ctx;
    } s;
+   enum cipher_mode mode = info->mode & cm_modes;
 
-   cipher = find_cipher(info->algo);
-   if (cipher == -1) {
-      return CRYPT_INVALID_CIPHER;
+   if (mode != cm_stream) {
+      cipher = find_cipher(info->algo);
+      if (cipher == -1) {
+         return CRYPT_INVALID_CIPHER;
+      }
    }
 
    switch (info->mode) {
@@ -222,6 +231,50 @@ int pem_decrypt(unsigned char *data, unsigned long *datalen,
          }
 #else
          return CRYPT_INVALID_CIPHER;
+#endif
+         break;
+      case cm_gcm:
+#ifdef LTC_GCM_MODE
+         if ((err = gcm_memory(cipher,
+                               key, keylen,
+                               iv, ivlen,
+                               NULL, 0,
+                               data, *datalen, data,
+                               tag, &taglen,
+                               GCM_DECRYPT)) != CRYPT_OK) {
+            goto error_out;
+         }
+#else
+         LTC_UNUSED_PARAM(tag);
+         LTC_UNUSED_PARAM(taglen);
+         return CRYPT_INVALID_CIPHER;
+#endif
+         break;
+      case cm_stream:
+#ifdef LTC_CHACHA
+         LTC_ARGCHK(ivlen == 16);
+
+         if ((err = chacha_memory(key, keylen, 20,
+                                  iv, ivlen, 0,
+                                  data, *datalen, data)) != CRYPT_OK) {
+            goto error_out;
+         }
+#else
+         return CRYPT_INVALID_CIPHER;
+#endif
+         break;
+      case cm_stream_openssh:
+#ifdef LTC_CHACHA20POLY1305_MODE
+         if ((err = chacha20poly1305_memory(key, 32,
+                                            iv, ivlen,
+                                            NULL, 0,
+                                            data, *datalen, data,
+                                            tag, &taglen,
+                                            CHACHA20POLY1305_DECRYPT | CHACHA20POLY1305_OPENSSH_COMPAT)) != CRYPT_OK) {
+            goto error_out;
+         }
+#else
+         return CRYPT_INVALID_CIPHER;
 #endif
          break;
       default:

+ 2 - 6
src/misc/pem/pem_pkcs.c

@@ -16,12 +16,8 @@ static int s_decrypt_pem(unsigned char *pem, unsigned long *l, const struct pem_
 {
    unsigned char iv[MAXBLOCKSIZE], key[MAXBLOCKSIZE];
    unsigned long ivlen, klen;
-   int err, cipher;
+   int err;
 
-   cipher = find_cipher(hdr->info.algo);
-   if (cipher == -1) {
-      return CRYPT_INVALID_CIPHER;
-   }
    if (hdr->info.keylen > sizeof(key)) {
       return CRYPT_BUFFER_OVERFLOW;
    }
@@ -38,7 +34,7 @@ static int s_decrypt_pem(unsigned char *pem, unsigned long *l, const struct pem_
       return err;
    }
 
-   err = pem_decrypt(pem, l, key, klen, iv, ivlen, &hdr->info, LTC_PAD_PKCS7);
+   err = pem_decrypt(pem, l, key, klen, iv, ivlen, NULL, 0, &hdr->info, LTC_PAD_PKCS7);
 
    zeromem(key, sizeof(key));
    zeromem(iv, sizeof(iv));

+ 57 - 38
src/misc/pem/pem_ssh.c

@@ -17,31 +17,35 @@
  */
 const struct blockcipher_info ssh_ciphers[] =
 {
-   { .name = "none",            .algo = "",         .keylen = 0,       .mode = cm_none },
-   { .name = "aes128-cbc",      .algo = "aes",      .keylen = 128 / 8, .mode = cm_cbc  },
-   { .name = "aes128-ctr",      .algo = "aes",      .keylen = 128 / 8, .mode = cm_ctr  },
-   { .name = "aes192-cbc",      .algo = "aes",      .keylen = 192 / 8, .mode = cm_cbc  },
-   { .name = "aes192-ctr",      .algo = "aes",      .keylen = 192 / 8, .mode = cm_ctr  },
-   { .name = "aes256-cbc",      .algo = "aes",      .keylen = 256 / 8, .mode = cm_cbc  },
-   { .name = "aes256-ctr",      .algo = "aes",      .keylen = 256 / 8, .mode = cm_ctr  },
-   { .name = "blowfish128-cbc", .algo = "blowfish", .keylen = 128 / 8, .mode = cm_cbc  },
-   { .name = "blowfish128-ctr", .algo = "blowfish", .keylen = 128 / 8, .mode = cm_ctr  },
-   { .name = "des-cbc",         .algo = "des",      .keylen = 64 / 8,  .mode = cm_cbc  },
-   { .name = "3des-cbc",        .algo = "3des",     .keylen = 192 / 8, .mode = cm_cbc  },
-   { .name = "3des-ctr",        .algo = "3des",     .keylen = 192 / 8, .mode = cm_ctr  },
-   { .name = "serpent128-cbc",  .algo = "serpent",  .keylen = 128 / 8, .mode = cm_cbc  },
-   { .name = "serpent128-ctr",  .algo = "serpent",  .keylen = 128 / 8, .mode = cm_ctr  },
-   { .name = "serpent192-cbc",  .algo = "serpent",  .keylen = 192 / 8, .mode = cm_cbc  },
-   { .name = "serpent192-ctr",  .algo = "serpent",  .keylen = 192 / 8, .mode = cm_ctr  },
-   { .name = "serpent256-cbc",  .algo = "serpent",  .keylen = 256 / 8, .mode = cm_cbc  },
-   { .name = "serpent256-ctr",  .algo = "serpent",  .keylen = 256 / 8, .mode = cm_ctr  },
-   { .name = "twofish128-cbc",  .algo = "twofish",  .keylen = 128 / 8, .mode = cm_cbc  },
-   { .name = "twofish128-ctr",  .algo = "twofish",  .keylen = 128 / 8, .mode = cm_ctr  },
-   { .name = "twofish192-cbc",  .algo = "twofish",  .keylen = 192 / 8, .mode = cm_cbc  },
-   { .name = "twofish192-ctr",  .algo = "twofish",  .keylen = 192 / 8, .mode = cm_ctr  },
-   { .name = "twofish-cbc",     .algo = "twofish",  .keylen = 256 / 8, .mode = cm_cbc  },
-   { .name = "twofish256-cbc",  .algo = "twofish",  .keylen = 256 / 8, .mode = cm_cbc  },
-   { .name = "twofish256-ctr",  .algo = "twofish",  .keylen = 256 / 8, .mode = cm_ctr  },
+   { .name = "none",                          .algo = "",         .keylen = 0,       .mode = cm_none                },
+   { .name = "aes128-cbc",                    .algo = "aes",      .keylen = 128 / 8, .mode = cm_cbc                 },
+   { .name = "aes128-ctr",                    .algo = "aes",      .keylen = 128 / 8, .mode = cm_ctr                 },
+   { .name = "aes192-cbc",                    .algo = "aes",      .keylen = 192 / 8, .mode = cm_cbc                 },
+   { .name = "aes192-ctr",                    .algo = "aes",      .keylen = 192 / 8, .mode = cm_ctr                 },
+   { .name = "aes256-cbc",                    .algo = "aes",      .keylen = 256 / 8, .mode = cm_cbc                 },
+   { .name = "aes256-ctr",                    .algo = "aes",      .keylen = 256 / 8, .mode = cm_ctr                 },
+   { .name = "[email protected]",        .algo = "aes",      .keylen = 128 / 8, .mode = cm_gcm                 },
+   { .name = "[email protected]",        .algo = "aes",      .keylen = 256 / 8, .mode = cm_gcm                 },
+   { .name = "blowfish128-cbc",               .algo = "blowfish", .keylen = 128 / 8, .mode = cm_cbc                 },
+   { .name = "blowfish128-ctr",               .algo = "blowfish", .keylen = 128 / 8, .mode = cm_ctr                 },
+   /* The algo name doesn't matter, it's only used in pem-info */
+   { .name = "[email protected]", .algo = "c20p1305", .keylen = 256 / 8, .mode = cm_stream | cm_openssh },
+   { .name = "des-cbc",                       .algo = "des",      .keylen = 64 / 8,  .mode = cm_cbc                 },
+   { .name = "3des-cbc",                      .algo = "3des",     .keylen = 192 / 8, .mode = cm_cbc                 },
+   { .name = "3des-ctr",                      .algo = "3des",     .keylen = 192 / 8, .mode = cm_ctr                 },
+   { .name = "serpent128-cbc",                .algo = "serpent",  .keylen = 128 / 8, .mode = cm_cbc                 },
+   { .name = "serpent128-ctr",                .algo = "serpent",  .keylen = 128 / 8, .mode = cm_ctr                 },
+   { .name = "serpent192-cbc",                .algo = "serpent",  .keylen = 192 / 8, .mode = cm_cbc                 },
+   { .name = "serpent192-ctr",                .algo = "serpent",  .keylen = 192 / 8, .mode = cm_ctr                 },
+   { .name = "serpent256-cbc",                .algo = "serpent",  .keylen = 256 / 8, .mode = cm_cbc                 },
+   { .name = "serpent256-ctr",                .algo = "serpent",  .keylen = 256 / 8, .mode = cm_ctr                 },
+   { .name = "twofish128-cbc",                .algo = "twofish",  .keylen = 128 / 8, .mode = cm_cbc                 },
+   { .name = "twofish128-ctr",                .algo = "twofish",  .keylen = 128 / 8, .mode = cm_ctr                 },
+   { .name = "twofish192-cbc",                .algo = "twofish",  .keylen = 192 / 8, .mode = cm_cbc                 },
+   { .name = "twofish192-ctr",                .algo = "twofish",  .keylen = 192 / 8, .mode = cm_ctr                 },
+   { .name = "twofish-cbc",                   .algo = "twofish",  .keylen = 256 / 8, .mode = cm_cbc                 },
+   { .name = "twofish256-cbc",                .algo = "twofish",  .keylen = 256 / 8, .mode = cm_cbc                 },
+   { .name = "twofish256-ctr",                .algo = "twofish",  .keylen = 256 / 8, .mode = cm_ctr                 },
 };
 const unsigned long ssh_ciphers_num = sizeof(ssh_ciphers)/sizeof(ssh_ciphers[0]);
 
@@ -403,21 +407,33 @@ static int s_decode_key(const unsigned char *in, unsigned long *inlen, ltc_pka_k
    return remaining ? padding_depad(p, &remaining, LTC_PAD_SSH) : CRYPT_OK;
 }
 
-static int s_decrypt_private_keys(unsigned char *in, unsigned long *inlen, struct kdf_options *opts)
+static int s_decrypt_private_keys(unsigned char *in, unsigned long *inlen,
+                                  unsigned char *tag, unsigned long taglen,
+                                  struct kdf_options *opts)
 {
    int err, cipher;
-   unsigned long symkey_len;
-   unsigned char symkey[MAXBLOCKSIZE];
+   unsigned long symkey_len, iv_len;
+   unsigned char symkey[MAXBLOCKSIZE], *iv, iv_[8] = { 0 };
+   enum cipher_mode mode = opts->cipher->mode & cm_modes;
 
    LTC_ARGCHK(in    != NULL);
    LTC_ARGCHK(inlen != NULL);
    LTC_ARGCHK(opts  != NULL);
 
-   cipher = find_cipher(opts->cipher->algo);
-   if (cipher == -1) {
-      return CRYPT_INVALID_CIPHER;
+   if (mode != cm_stream) {
+      cipher = find_cipher(opts->cipher->algo);
+      if (cipher == -1) {
+         return CRYPT_INVALID_CIPHER;
+      }
+
+      iv = symkey + opts->cipher->keylen;
+      iv_len = mode == cm_gcm ? 12 : cipher_descriptor[cipher].block_length;
+      symkey_len = opts->cipher->keylen + iv_len;
+   } else {
+      iv = iv_;
+      iv_len = sizeof(iv_);
+      symkey_len = 64;
    }
-   symkey_len = opts->cipher->keylen + cipher_descriptor[cipher].block_length;
 
    if (sizeof(symkey) < symkey_len) {
       return CRYPT_OVERFLOW;
@@ -427,12 +443,11 @@ static int s_decrypt_private_keys(unsigned char *in, unsigned long *inlen, struc
                                    opts->num_rounds, find_hash("sha512"), symkey, &symkey_len)) != CRYPT_OK) {
       return err;
    }
-
    err = pem_decrypt(in, inlen,
                      symkey, opts->cipher->keylen,
-                     symkey + opts->cipher->keylen, cipher_descriptor[cipher].block_length,
+                     iv, iv_len,
+                     tag, taglen,
                      opts->cipher, LTC_PAD_SSH);
-
    zeromem(symkey, sizeof(symkey));
 
    return err;
@@ -522,8 +537,8 @@ static const unsigned long pem_openssh_num = sizeof(pem_openssh)/sizeof(pem_open
 
 static int s_decode_openssh(struct get_char *g, ltc_pka_key *k, const password_ctx *pw_ctx)
 {
-   unsigned char *pem = NULL, *p, *privkey = NULL;
-   unsigned long n, w, l, privkey_len;
+   unsigned char *pem = NULL, *p, *privkey = NULL, *tag;
+   unsigned long n, w, l, privkey_len, taglen;
    int err;
    struct pem_headers hdr;
    struct kdf_options opts = { 0 };
@@ -575,8 +590,12 @@ retry:
             err = CRYPT_ERROR;
             goto cleanup;
          }
+         tag = p + w;
+         taglen = l - w;
          w = privkey_len;
-         if ((err = s_decrypt_private_keys(privkey, &privkey_len, &opts)) != CRYPT_OK) {
+         if ((err = s_decrypt_private_keys(privkey, &privkey_len,
+                                           tag, taglen,
+                                           &opts)) != CRYPT_OK) {
             goto cleanup;
          }
          zeromem(opts.pw.pw, opts.pw.l);

+ 15 - 5
src/stream/chacha/chacha_memory.c

@@ -9,11 +9,12 @@
    Encrypt (or decrypt) bytes of ciphertext (or plaintext) with ChaCha
    @param key     The key
    @param keylen  The key length
+   @param rounds  The number of rounds
    @param iv      The initial vector
    @param ivlen   The initial vector length
+   @param counter initial counter value, either ignored, 32- or 64-bit, depending on ivlen
    @param datain  The plaintext (or ciphertext)
    @param datalen The length of the input and output (octets)
-   @param rounds  The number of rounds
    @param dataout [out] The ciphertext (or plaintext)
    @return CRYPT_OK if successful
 */
@@ -23,14 +24,23 @@ int chacha_memory(const unsigned char *key,    unsigned long keylen,  unsigned l
 {
    chacha_state st;
    int err;
+   const unsigned char *iv_ = iv;
+   unsigned long ivlen_ = ivlen;
+   ulong64 counter_ = counter;
+
+   if (ivlen == 16) {
+      LOAD64L(counter_, iv);
+      iv_ += 8;
+      ivlen_ -=8;
+   }
 
-   LTC_ARGCHK(ivlen <= 8 || counter < 4294967296);       /* 2**32 */
+   LTC_ARGCHK(ivlen_ <= 8 || counter_ < CONST64(4294967296));       /* 2**32 */
 
    if ((err = chacha_setup(&st, key, keylen, rounds))       != CRYPT_OK) goto WIPE_KEY;
-   if (ivlen > 8) {
-        if ((err = chacha_ivctr32(&st, iv, ivlen, (ulong32)counter)) != CRYPT_OK) goto WIPE_KEY;
+   if (ivlen_ > 8) {
+        if ((err = chacha_ivctr32(&st, iv_, ivlen_, (ulong32)counter_)) != CRYPT_OK) goto WIPE_KEY;
    } else {
-        if ((err = chacha_ivctr64(&st, iv, ivlen, counter)) != CRYPT_OK) goto WIPE_KEY;
+        if ((err = chacha_ivctr64(&st, iv_, ivlen_, counter_)) != CRYPT_OK) goto WIPE_KEY;
    }
    err = chacha_crypt(&st, datain, datalen, dataout);
 WIPE_KEY:

+ 0 - 0
tests/pem/pkcs/unsupported/rsa-chacha20.pem → tests/pem/pkcs/rsa-chacha20.pem


+ 0 - 0
tests/pem/pkcs/unsupported/rsa-des-ede-cbc.pem → tests/pem/pkcs/rsa-des-ede-cbc.pem


+ 0 - 0
tests/pem/pkcs/unsupported/rsa-des-ede-cfb.pem → tests/pem/pkcs/rsa-des-ede-cfb.pem


+ 0 - 0
tests/pem/pkcs/unsupported/rsa-des-ede-ofb.pem → tests/pem/pkcs/rsa-des-ede-ofb.pem


+ 0 - 0
tests/pem/pkcs/unsupported/rsa-desx-cbc.pem → tests/pem/pkcs/rsa-desx-cbc.pem


+ 0 - 0
tests/pem/pubkeys/ssh_dsa_1024_openssh.pub → tests/pem/pubkeys/authorized_keys/ssh_dsa_1024_openssh.pub


+ 0 - 0
tests/pem/pubkeys/ssh_ecdsa_256_openssh.pub → tests/pem/pubkeys/authorized_keys/ssh_ecdsa_256_openssh.pub


+ 0 - 0
tests/pem/pubkeys/ssh_ecdsa_384_openssh.pub → tests/pem/pubkeys/authorized_keys/ssh_ecdsa_384_openssh.pub


+ 0 - 0
tests/pem/pubkeys/ssh_ecdsa_521_openssh.pub → tests/pem/pubkeys/authorized_keys/ssh_ecdsa_521_openssh.pub


+ 0 - 0
tests/pem/pubkeys/ssh_ed25519_openssh.pub → tests/pem/pubkeys/authorized_keys/ssh_ed25519_openssh.pub


+ 0 - 0
tests/pem/pubkeys/ssh_rsa_1024_openssh.pub → tests/pem/pubkeys/authorized_keys/ssh_rsa_1024_openssh.pub


+ 0 - 0
tests/pem/pubkeys/ssh_rsa_1536_openssh.pub → tests/pem/pubkeys/authorized_keys/ssh_rsa_1536_openssh.pub


+ 0 - 0
tests/pem/pubkeys/ssh_rsa_2048_openssh.pub → tests/pem/pubkeys/authorized_keys/ssh_rsa_2048_openssh.pub


+ 0 - 0
tests/pem/pubkeys/ssh_rsa_4096_openssh.pub → tests/pem/pubkeys/authorized_keys/ssh_rsa_4096_openssh.pub


+ 0 - 0
tests/pem/pubkeys/ssh_rsa_768_openssh.pub → tests/pem/pubkeys/authorized_keys/ssh_rsa_768_openssh.pub


+ 0 - 0
tests/pem/pubkeys/ssh_rsa_8192_openssh.pub → tests/pem/pubkeys/authorized_keys/ssh_rsa_8192_openssh.pub


+ 0 - 0
tests/pem/ssh/unsupported/ssh-rsa-3des-cbc.pub → tests/pem/ssh/authorized_keys/ssh-rsa-3des-cbc.pub


+ 0 - 0
tests/pem/ssh/unsupported/ssh-rsa-aes128-cbc.pub → tests/pem/ssh/authorized_keys/ssh-rsa-aes128-cbc.pub


+ 0 - 0
tests/pem/ssh/unsupported/ssh-rsa-aes128-ctr.pub → tests/pem/ssh/authorized_keys/ssh-rsa-aes128-ctr.pub


+ 0 - 0
tests/pem/ssh/unsupported/ssh-rsa-aes128-gcm.pub → tests/pem/ssh/authorized_keys/ssh-rsa-aes128-gcm.pub


+ 0 - 0
tests/pem/ssh/unsupported/ssh-rsa-aes192-cbc.pub → tests/pem/ssh/authorized_keys/ssh-rsa-aes192-cbc.pub


+ 0 - 0
tests/pem/ssh/unsupported/ssh-rsa-aes192-ctr.pub → tests/pem/ssh/authorized_keys/ssh-rsa-aes192-ctr.pub


+ 0 - 0
tests/pem/ssh/unsupported/ssh-rsa-aes256-cbc.pub → tests/pem/ssh/authorized_keys/ssh-rsa-aes256-cbc.pub


+ 0 - 0
tests/pem/ssh/unsupported/ssh-rsa-aes256-ctr.pub → tests/pem/ssh/authorized_keys/ssh-rsa-aes256-ctr.pub


+ 0 - 0
tests/pem/ssh/unsupported/ssh-rsa-aes256-gcm.pub → tests/pem/ssh/authorized_keys/ssh-rsa-aes256-gcm.pub


+ 0 - 0
tests/pem/ssh/unsupported/ssh-rsa-chacha20-poly1305.pub → tests/pem/ssh/authorized_keys/ssh-rsa-chacha20-poly1305.pub


+ 0 - 0
tests/pem/ssh/unsupported/ssh-rsa-aes128-gcm → tests/pem/ssh/ssh-rsa-aes128-gcm


+ 0 - 0
tests/pem/ssh/unsupported/ssh-rsa-aes256-gcm → tests/pem/ssh/ssh-rsa-aes256-gcm


+ 0 - 0
tests/pem/ssh/unsupported/ssh-rsa-chacha20-poly1305 → tests/pem/ssh/ssh-rsa-chacha20-poly1305


+ 2 - 0
tests/pem_test.c

@@ -141,6 +141,8 @@ int pem_test(void)
    DO(test_process_dir("tests/pem/ssh", &key, s_pem_decode_ssh, NULL, (dir_cleanup_cb)pka_key_free, "pem_ssh_test"));
    DO(test_process_dir("tests/pem/ssh", &key, NULL, s_pem_decode_ssh_f, (dir_cleanup_cb)pka_key_free, "pem_ssh_test_filehandle"));
    DO(test_process_dir("tests/pem/ssh/extra", &key, s_pem_decode_ssh, NULL, (dir_cleanup_cb)pka_key_free, "pem_ssh_test+extra"));
+   DO(test_process_dir("tests/pem/pubkeys", &key, s_pem_only_decode, NULL, (dir_cleanup_cb)pka_key_free, "pem_pubkeys_test"));
+   DO(test_process_dir("tests/pem/pubkeys", &key, NULL, s_pem_only_decode_f, (dir_cleanup_cb)pka_key_free, "pem_pubkeys_test_filehandle"));
 #endif
    DO(test_process_dir("tests/pem", &key, s_pem_only_decode, NULL, (dir_cleanup_cb)pka_key_free, "pem_test"));
    DO(test_process_dir("tests/pem", &key, NULL, s_pem_only_decode_f, (dir_cleanup_cb)pka_key_free, "pem_test_filehandle"));

+ 1 - 0
tests/test.c

@@ -149,6 +149,7 @@ static void s_unregister_all(void)
 #endif
 #ifdef LTC_DES
   unregister_cipher(&des_desc);
+  unregister_cipher(&desx_desc);
   unregister_cipher(&des3_desc);
 #endif
 #ifdef LTC_CAST5