From ce645060fa301b846d20f0b207d20e42b26010cf Mon Sep 17 00:00:00 2001 From: Felipe Moura Date: Tue, 14 Jul 2026 23:45:04 -0300 Subject: [PATCH] crypto: add CRYPTO_AES_CTR_SSH variant (128-bit big-endian counter) The existing CRYPTO_AES_CTR is the RFC 3686 profile: the last 4 bytes of the key are a nonce, the IV is 8 bytes and only the low 32 bits of the counter block are incremented. SSH aes128/192/256-ctr (RFC 4344) instead uses the key as-is (no embedded nonce) and treats the whole 16-byte IV as the initial counter block, incremented as a 128-bit big-endian integer, with the first keystream block being E(IV). Add CRYPTO_AES_CTR_SSH as a new enc_xform mirroring the CRYPTO_CHACHA20_DJB addition. It reuses struct aes_ctr_ctx and the AES block; only setkey (full key, no nonce), reinit (full 16-byte counter) and crypt (encrypt-then- increment over all 16 bytes) differ from the RFC 3686 variant. Keystream validated against `openssl enc -aes-128-ctr`, including a counter that carries across byte boundaries and a non-block-aligned tail. Signed-off-by: Felipe Moura --- crypto/cryptodev.c | 1 + crypto/cryptosoft.c | 6 ++++ crypto/xform.c | 68 ++++++++++++++++++++++++++++++++++++++ include/crypto/cryptodev.h | 3 +- include/crypto/xform.h | 1 + 5 files changed, 78 insertions(+), 1 deletion(-) diff --git a/crypto/cryptodev.c b/crypto/cryptodev.c index d624e14d330..7d57202a9c0 100644 --- a/crypto/cryptodev.c +++ b/crypto/cryptodev.c @@ -232,6 +232,7 @@ static int cryptof_ioctl(FAR struct file *filep, case CRYPTO_AES_256_CBC: case CRYPTO_AES_CMAC: case CRYPTO_AES_CTR: + case CRYPTO_AES_CTR_SSH: case CRYPTO_AES_XTS: case CRYPTO_AES_OFB: case CRYPTO_AES_CFB_8: diff --git a/crypto/cryptosoft.c b/crypto/cryptosoft.c index c1f4539b060..a3d10c48765 100644 --- a/crypto/cryptosoft.c +++ b/crypto/cryptosoft.c @@ -1619,6 +1619,9 @@ int swcr_newsession(FAR uint32_t *sid, FAR struct cryptoini *cri) case CRYPTO_AES_CTR: txf = &enc_xform_aes_ctr; goto enccommon; + case CRYPTO_AES_CTR_SSH: + txf = &enc_xform_aes_ctr_ssh; + goto enccommon; case CRYPTO_AES_XTS: txf = &enc_xform_aes_xts; goto enccommon; @@ -1903,6 +1906,7 @@ int swcr_freesession(uint64_t tid) case CRYPTO_CAST_CBC: case CRYPTO_RIJNDAEL128_CBC: case CRYPTO_AES_CTR: + case CRYPTO_AES_CTR_SSH: case CRYPTO_AES_XTS: case CRYPTO_AES_GCM_16: case CRYPTO_AES_GMAC: @@ -2046,6 +2050,7 @@ int swcr_process(struct cryptop *crp) case CRYPTO_CAST_CBC: case CRYPTO_RIJNDAEL128_CBC: case CRYPTO_AES_CTR: + case CRYPTO_AES_CTR_SSH: case CRYPTO_AES_XTS: case CRYPTO_AES_OFB: case CRYPTO_AES_CFB_8: @@ -2442,6 +2447,7 @@ void swcr_init(void) algs[CRYPTO_RIPEMD160_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED; algs[CRYPTO_RIJNDAEL128_CBC] = CRYPTO_ALG_FLAG_SUPPORTED; algs[CRYPTO_AES_CTR] = CRYPTO_ALG_FLAG_SUPPORTED; + algs[CRYPTO_AES_CTR_SSH] = CRYPTO_ALG_FLAG_SUPPORTED; algs[CRYPTO_AES_XTS] = CRYPTO_ALG_FLAG_SUPPORTED; algs[CRYPTO_AES_GCM_16] = CRYPTO_ALG_FLAG_SUPPORTED; algs[CRYPTO_AES_GMAC] = CRYPTO_ALG_FLAG_SUPPORTED; diff --git a/crypto/xform.c b/crypto/xform.c index 788a718b08e..b412566ae24 100644 --- a/crypto/xform.c +++ b/crypto/xform.c @@ -108,6 +108,7 @@ int blf_setkey(FAR void *, FAR uint8_t *, int); int cast5_setkey(FAR void *, FAR uint8_t *, int); int aes_setkey_xform(FAR void *, FAR uint8_t *, int); int aes_ctr_setkey(FAR void *, FAR uint8_t *, int); +int aes_ctr_ssh_setkey(FAR void *, FAR uint8_t *, int); int aes_xts_setkey(FAR void *, FAR uint8_t *, int); int aes_ofb_setkey(FAR void *, FAR uint8_t *, int); int null_setkey(FAR void *, FAR uint8_t *, int); @@ -132,8 +133,10 @@ void aes_cfb8_decrypt(caddr_t, FAR uint8_t *, size_t); void aes_cfb128_decrypt(caddr_t, FAR uint8_t *, size_t); void aes_ctr_crypt(caddr_t, FAR uint8_t *, size_t); +void aes_ctr_ssh_crypt(caddr_t, FAR uint8_t *, size_t); void aes_ctr_reinit(caddr_t, FAR uint8_t *); +void aes_ctr_ssh_reinit(caddr_t, FAR uint8_t *); void aes_xts_reinit(caddr_t, FAR uint8_t *); void aes_gcm_reinit(caddr_t, FAR uint8_t *); void aes_ofb_reinit(caddr_t, FAR uint8_t *); @@ -232,6 +235,17 @@ const struct enc_xform enc_xform_aes_ctr = aes_ctr_reinit }; +const struct enc_xform enc_xform_aes_ctr_ssh = +{ + CRYPTO_AES_CTR_SSH, "AES-CTR-SSH", + 16, 16, 16, 32, + sizeof(struct aes_ctr_ctx), + aes_ctr_ssh_crypt, + aes_ctr_ssh_crypt, + aes_ctr_ssh_setkey, + aes_ctr_ssh_reinit +}; + const struct enc_xform enc_xform_aes_gcm = { CRYPTO_AES_GCM_16, "AES-GCM", @@ -697,6 +711,60 @@ int aes_ctr_setkey(FAR void *sched, FAR uint8_t *key, int len) return 0; } +/* SSH AES-CTR (RFC 4344). Unlike the RFC 3686 variant above, the key holds + * no embedded nonce and the whole 16-byte IV is the initial counter block, + * incremented as a 128-bit big-endian integer. The counter is encrypted + * before it is incremented so the first block keystream is E(IV). + */ + +void aes_ctr_ssh_crypt(caddr_t key, FAR uint8_t *data, size_t len) +{ + FAR struct aes_ctr_ctx *ctx; + uint8_t keystream[AESCTR_BLOCKSIZE]; + int i; + + ctx = (FAR struct aes_ctr_ctx *)key; + + aes_encrypt(&ctx->ac_key, ctx->ac_block, keystream); + for (i = 0; i < AESCTR_BLOCKSIZE; i++) + { + data[i] ^= keystream[i]; + } + + /* increment the 128-bit big-endian counter */ + + for (i = AESCTR_BLOCKSIZE - 1; i >= 0; i--) + { + if (++ctx->ac_block[i]) + { + break; + } + } + + explicit_bzero(keystream, sizeof(keystream)); +} + +int aes_ctr_ssh_setkey(FAR void *sched, FAR uint8_t *key, int len) +{ + FAR struct aes_ctr_ctx *ctx; + + ctx = (FAR struct aes_ctr_ctx *)sched; + if (aes_setkey(&ctx->ac_key, key, len) != 0) + { + return -1; + } + + return 0; +} + +void aes_ctr_ssh_reinit(caddr_t key, FAR uint8_t *iv) +{ + FAR struct aes_ctr_ctx *ctx; + + ctx = (FAR struct aes_ctr_ctx *)key; + bcopy(iv, ctx->ac_block, AESCTR_BLOCKSIZE); +} + void aes_xts_reinit(caddr_t key, FAR uint8_t *iv) { FAR struct aes_xts_ctx *ctx = (FAR struct aes_xts_ctx *)key; diff --git a/include/crypto/cryptodev.h b/include/crypto/cryptodev.h index 824f8fafb12..cefe056cedf 100644 --- a/include/crypto/cryptodev.h +++ b/include/crypto/cryptodev.h @@ -142,7 +142,8 @@ #define CRYPTO_SHA2_224_HMAC 41 #define CRYPTO_CHACHA20 42 #define CRYPTO_CHACHA20_DJB 43 -#define CRYPTO_ALGORITHM_MAX 43 /* Keep updated */ +#define CRYPTO_AES_CTR_SSH 44 +#define CRYPTO_ALGORITHM_MAX 44 /* Keep updated */ /* Algorithm flags */ diff --git a/include/crypto/xform.h b/include/crypto/xform.h index ad6b7ad4086..95e7600b558 100644 --- a/include/crypto/xform.h +++ b/include/crypto/xform.h @@ -105,6 +105,7 @@ extern const struct enc_xform enc_xform_blf; extern const struct enc_xform enc_xform_cast5; extern const struct enc_xform enc_xform_aes; extern const struct enc_xform enc_xform_aes_ctr; +extern const struct enc_xform enc_xform_aes_ctr_ssh; extern const struct enc_xform enc_xform_aes_gcm; extern const struct enc_xform enc_xform_aes_gmac; extern const struct enc_xform enc_xform_aes_cmac;