Chacha20-Poly1305 encryption

This commit implements the Chacha20-Poly1305 AEAD from RFC 8439 as a new
algorithm option for encrypted datasets.

AES (and particularly the default AES-GCM mode used in OpenZFS) is known
to be very slow on systems without hardware assistance. There are many
such machines out there could make good use of OpenZFS, especially
low-cost machines and small boards that would otherwise make very nice
storage machines. The Raspberry Pi series of machines are a good
example.

The best option for these systems is an encryption option that performs
well in software. Chacha20-Poly1305 is the current "standard" option for
this in many contexts, and is a good choice for OpenZFS.

The core Chacha20 and Poly1305 implementations are taken from Loup
Valliant's Monocypher. These were chosen because they are compact, easy
to read, easy to use and the author has written extensively about its
development, all of which give me confidence that there are unlikely to
be any surprises.

I've added a KCF-style module to the ICP to implement the AEAD. This
implements just enough for OpenZFS, and is not suitable as a
general-purpose KCF for Illumos (though it could be the starting point
for one).

For FreeBSD, which does not use the ICP, I've instead hooked it up to
FreeBSD's builtin crypto stack.

The rest is adding an enabling property value and a feature flag and and
hooking it up to all the right touch points, and documentation updates.

The existing tests that cycle through the possible encryption options
have been extended to add one more.

I've added a test to ensure that raw receives of chacha20-poly1305
datasets do the right thing based on the state of the feature flag on
the receiving side.

There's also a test unit that runs the test vectors in RFC 8439 against
Chacha20, Poly1305 and the AEAD in the ICP that combines them. This is
most useful as a sanity check during future work to add alternate
(accelerated) implementations.

Finally, manual interop testing has been done to confirm that pools and
streams can be moved between Linux and FreeBSD correctly.

Light and uncontrolled performance testing on a Raspberry Pi 4B
(Broadcom BCM2711, no hardware AES) writing to a chacha20-poly1305
dataset was ~2.4x faster than aes-256-gcm on the same hardware. On a
Fitlet2 (Celeron J3455, AES-NI but no AVX (#10846)) it was ~1.3x faster.

Sponsored-by: https://despairlabs.com/sponsor/
Signed-off-by: Rob Norris <robn@despairlabs.com>
This commit is contained in:
Rob Norris 2023-09-18 21:25:43 +10:00
parent d4d79451cb
commit d9d69c1b97
33 changed files with 2051 additions and 40 deletions

View File

@ -124,6 +124,7 @@ cstyle:
! -name 'zfs_config.*' ! -name '*.mod.c' \ ! -name 'zfs_config.*' ! -name '*.mod.c' \
! -name 'opt_global.h' ! -name '*_if*.h' \ ! -name 'opt_global.h' ! -name '*_if*.h' \
! -name 'zstd_compat_wrapper.h' \ ! -name 'zstd_compat_wrapper.h' \
! -name 'monocypher.[ch]' \
! -path './module/zstd/lib/*' \ ! -path './module/zstd/lib/*' \
! -path './include/sys/lua/*' \ ! -path './include/sys/lua/*' \
! -path './module/lua/l*.[ch]' \ ! -path './module/lua/l*.[ch]' \

View File

@ -41,6 +41,7 @@
#define SUN_CKM_AES_CCM "CKM_AES_CCM" #define SUN_CKM_AES_CCM "CKM_AES_CCM"
#define SUN_CKM_AES_GCM "CKM_AES_GCM" #define SUN_CKM_AES_GCM "CKM_AES_GCM"
#define SUN_CKM_SHA512_HMAC "CKM_SHA512_HMAC" #define SUN_CKM_SHA512_HMAC "CKM_SHA512_HMAC"
#define SUN_CKM_CHACHA20_POLY1305 "CKM_CHACHA20_POLY1305"
#define CRYPTO_BITS2BYTES(n) ((n) == 0 ? 0 : (((n) - 1) >> 3) + 1) #define CRYPTO_BITS2BYTES(n) ((n) == 0 ? 0 : (((n) - 1) >> 3) + 1)
#define CRYPTO_BYTES2BITS(n) ((n) << 3) #define CRYPTO_BYTES2BITS(n) ((n) << 3)

View File

@ -82,6 +82,7 @@ typedef uint32_t crypto_keysize_unit_t;
#define SUN_CKM_SHA512_HMAC "CKM_SHA512_HMAC" #define SUN_CKM_SHA512_HMAC "CKM_SHA512_HMAC"
#define SUN_CKM_AES_CCM "CKM_AES_CCM" #define SUN_CKM_AES_CCM "CKM_AES_CCM"
#define SUN_CKM_AES_GCM "CKM_AES_GCM" #define SUN_CKM_AES_GCM "CKM_AES_GCM"
#define SUN_CKM_CHACHA20_POLY1305 "CKM_CHACHA20_POLY1305"
/* Data arguments of cryptographic operations */ /* Data arguments of cryptographic operations */

View File

@ -26,6 +26,9 @@
#ifndef _SYS_CRYPTO_ALGS_H #ifndef _SYS_CRYPTO_ALGS_H
#define _SYS_CRYPTO_ALGS_H #define _SYS_CRYPTO_ALGS_H
int chapoly_mod_init(void);
int chapoly_mod_fini(void);
int aes_mod_init(void); int aes_mod_init(void);
int aes_mod_fini(void); int aes_mod_fini(void);

View File

@ -1906,6 +1906,7 @@ enum zio_encrypt {
ZIO_CRYPT_AES_128_GCM, ZIO_CRYPT_AES_128_GCM,
ZIO_CRYPT_AES_192_GCM, ZIO_CRYPT_AES_192_GCM,
ZIO_CRYPT_AES_256_GCM, ZIO_CRYPT_AES_256_GCM,
ZIO_CRYPT_CHACHA20_POLY1305,
ZIO_CRYPT_FUNCTIONS ZIO_CRYPT_FUNCTIONS
}; };

View File

@ -45,7 +45,8 @@ struct zbookmark_phys;
typedef enum zio_crypt_type { typedef enum zio_crypt_type {
ZC_TYPE_NONE = 0, ZC_TYPE_NONE = 0,
ZC_TYPE_CCM, ZC_TYPE_CCM,
ZC_TYPE_GCM ZC_TYPE_GCM,
ZC_TYPE_CHACHA20_POLY1305,
} zio_crypt_type_t; } zio_crypt_type_t;
/* table of supported crypto algorithms, modes and keylengths. */ /* table of supported crypto algorithms, modes and keylengths. */
@ -60,7 +61,7 @@ typedef struct zio_crypt_info {
#else #else
crypto_mech_name_t ci_mechname; crypto_mech_name_t ci_mechname;
#endif #endif
/* cipher mode type (GCM, CCM) */ /* cipher mode type (GCM, CCM, ChaCha20-Poly1305) */
zio_crypt_type_t ci_crypt_type; zio_crypt_type_t ci_crypt_type;
/* length of the encryption key */ /* length of the encryption key */

View File

@ -83,6 +83,7 @@ typedef enum spa_feature {
SPA_FEATURE_REDACTION_LIST_SPILL, SPA_FEATURE_REDACTION_LIST_SPILL,
SPA_FEATURE_RAIDZ_EXPANSION, SPA_FEATURE_RAIDZ_EXPANSION,
SPA_FEATURE_FAST_DEDUP, SPA_FEATURE_FAST_DEDUP,
SPA_FEATURE_CHACHA20_POLY1305,
SPA_FEATURES SPA_FEATURES
} spa_feature_t; } spa_feature_t;

View File

@ -29,8 +29,10 @@ nodist_libicp_la_SOURCES = \
module/icp/algs/skein/skein_block.c \ module/icp/algs/skein/skein_block.c \
module/icp/algs/skein/skein_iv.c \ module/icp/algs/skein/skein_iv.c \
module/icp/illumos-crypto.c \ module/icp/illumos-crypto.c \
module/icp/monocypher.c \
module/icp/io/aes.c \ module/icp/io/aes.c \
module/icp/io/sha2_mod.c \ module/icp/io/sha2_mod.c \
module/icp/io/chapoly.c \
module/icp/core/kcf_sched.c \ module/icp/core/kcf_sched.c \
module/icp/core/kcf_prov_lib.c \ module/icp/core/kcf_prov_lib.c \
module/icp/core/kcf_callprov.c \ module/icp/core/kcf_callprov.c \

View File

@ -618,7 +618,7 @@
<elf-symbol name='fletcher_4_superscalar_ops' size='128' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> <elf-symbol name='fletcher_4_superscalar_ops' size='128' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='libzfs_config_ops' size='16' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> <elf-symbol name='libzfs_config_ops' size='16' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='sa_protocol_names' size='16' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> <elf-symbol name='sa_protocol_names' size='16' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='spa_feature_table' size='2352' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> <elf-symbol name='spa_feature_table' size='2408' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='zfeature_checks_disable' size='4' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> <elf-symbol name='zfeature_checks_disable' size='4' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='zfs_deleg_perm_tab' size='512' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> <elf-symbol name='zfs_deleg_perm_tab' size='512' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='zfs_history_event_names' size='328' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> <elf-symbol name='zfs_history_event_names' size='328' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
@ -6017,7 +6017,8 @@
<enumerator name='SPA_FEATURE_REDACTION_LIST_SPILL' value='39'/> <enumerator name='SPA_FEATURE_REDACTION_LIST_SPILL' value='39'/>
<enumerator name='SPA_FEATURE_RAIDZ_EXPANSION' value='40'/> <enumerator name='SPA_FEATURE_RAIDZ_EXPANSION' value='40'/>
<enumerator name='SPA_FEATURE_FAST_DEDUP' value='41'/> <enumerator name='SPA_FEATURE_FAST_DEDUP' value='41'/>
<enumerator name='SPA_FEATURES' value='42'/> <enumerator name='SPA_FEATURE_CHACHA20_POLY1305' value='42'/>
<enumerator name='SPA_FEATURES' value='43'/>
</enum-decl> </enum-decl>
<typedef-decl name='spa_feature_t' type-id='33ecb627' id='d6618c78'/> <typedef-decl name='spa_feature_t' type-id='33ecb627' id='d6618c78'/>
<qualified-type-def type-id='80f4b756' const='yes' id='b99c00c9'/> <qualified-type-def type-id='80f4b756' const='yes' id='b99c00c9'/>
@ -9181,8 +9182,8 @@
</function-decl> </function-decl>
</abi-instr> </abi-instr>
<abi-instr address-size='64' path='module/zcommon/zfeature_common.c' language='LANG_C99'> <abi-instr address-size='64' path='module/zcommon/zfeature_common.c' language='LANG_C99'>
<array-type-def dimensions='1' type-id='83f29ca2' size-in-bits='18816' id='b937914f'> <array-type-def dimensions='1' type-id='83f29ca2' size-in-bits='19264' id='bd39d632'>
<subrange length='42' type-id='7359adad' id='cb7c937f'/> <subrange length='43' type-id='7359adad' id='8f7e73a2'/>
</array-type-def> </array-type-def>
<enum-decl name='zfeature_flags' id='6db816a4'> <enum-decl name='zfeature_flags' id='6db816a4'>
<underlying-type type-id='9cac1fee'/> <underlying-type type-id='9cac1fee'/>
@ -9259,7 +9260,7 @@
<pointer-type-def type-id='611586a1' size-in-bits='64' id='2e243169'/> <pointer-type-def type-id='611586a1' size-in-bits='64' id='2e243169'/>
<qualified-type-def type-id='eaa32e2f' const='yes' id='83be723c'/> <qualified-type-def type-id='eaa32e2f' const='yes' id='83be723c'/>
<pointer-type-def type-id='83be723c' size-in-bits='64' id='7acd98a2'/> <pointer-type-def type-id='83be723c' size-in-bits='64' id='7acd98a2'/>
<var-decl name='spa_feature_table' type-id='b937914f' mangled-name='spa_feature_table' visibility='default' elf-symbol-id='spa_feature_table'/> <var-decl name='spa_feature_table' type-id='bd39d632' mangled-name='spa_feature_table' visibility='default' elf-symbol-id='spa_feature_table'/>
<var-decl name='zfeature_checks_disable' type-id='c19b74c3' mangled-name='zfeature_checks_disable' visibility='default' elf-symbol-id='zfeature_checks_disable'/> <var-decl name='zfeature_checks_disable' type-id='c19b74c3' mangled-name='zfeature_checks_disable' visibility='default' elf-symbol-id='zfeature_checks_disable'/>
<function-decl name='opendir' visibility='default' binding='global' size-in-bits='64'> <function-decl name='opendir' visibility='default' binding='global' size-in-bits='64'>
<parameter type-id='80f4b756'/> <parameter type-id='80f4b756'/>

View File

@ -37,8 +37,9 @@
.\" Copyright 2019 Joyent, Inc. .\" Copyright 2019 Joyent, Inc.
.\" Copyright (c) 2019, Kjeld Schouten-Lebbing .\" Copyright (c) 2019, Kjeld Schouten-Lebbing
.\" Copyright (c) 2022 Hewlett Packard Enterprise Development LP. .\" Copyright (c) 2022 Hewlett Packard Enterprise Development LP.
.\" Copyright (c) 2023, Rob Norris <robn@despairlabs.com>
.\" .\"
.Dd June 29, 2024 .Dd July 26, 2024
.Dt ZFSPROPS 7 .Dt ZFSPROPS 7
.Os .Os
. .
@ -1077,7 +1078,7 @@ This property can also be referred to by its shortened column name,
.It Xo .It Xo
.Sy encryption Ns = Ns Sy off Ns | Ns Sy on Ns | Ns Sy aes-128-ccm Ns | Ns .Sy encryption Ns = Ns Sy off Ns | Ns Sy on Ns | Ns Sy aes-128-ccm Ns | Ns
.Sy aes-192-ccm Ns | Ns Sy aes-256-ccm Ns | Ns Sy aes-128-gcm Ns | Ns .Sy aes-192-ccm Ns | Ns Sy aes-256-ccm Ns | Ns Sy aes-128-gcm Ns | Ns
.Sy aes-192-gcm Ns | Ns Sy aes-256-gcm .Sy aes-192-gcm Ns | Ns Sy aes-256-gcm Ns | Ns Sy chacha20-poly1305
.Xc .Xc
Controls the encryption cipher suite (block cipher, key length, and mode) used Controls the encryption cipher suite (block cipher, key length, and mode) used
for this dataset. for this dataset.
@ -1096,6 +1097,12 @@ selected, which is currently
In order to provide consistent data protection, encryption must be specified at In order to provide consistent data protection, encryption must be specified at
dataset creation time and it cannot be changed afterwards. dataset creation time and it cannot be changed afterwards.
.Pp .Pp
On systems lacking hardware-accelerated AES (many non-x86 boards)
.Sy chacha20-poly1305
will usually offer better performance without compromising security.
On x86, or when datasets may be mounted on on older versions of ZFS, an AES
suite is the best choice.
.Pp
For more details and caveats about encryption see the For more details and caveats about encryption see the
.Sx Encryption .Sx Encryption
section of section of

View File

@ -18,8 +18,9 @@
.\" Copyright (c) 2019, Allan Jude .\" Copyright (c) 2019, Allan Jude
.\" Copyright (c) 2021, Colm Buckley <colm@tuatha.org> .\" Copyright (c) 2021, Colm Buckley <colm@tuatha.org>
.\" Copyright (c) 2023, Klara Inc. .\" Copyright (c) 2023, Klara Inc.
.\" Copyright (c) 2024, Rob Norris <robn@despairlabs.com>
.\" .\"
.Dd February 14, 2024 .Dd August 23, 2024
.Dt ZPOOL-FEATURES 7 .Dt ZPOOL-FEATURES 7
.Os .Os
. .
@ -411,6 +412,21 @@ returned to the
.Sy enabled .Sy enabled
state when all bookmarks with these fields are destroyed. state when all bookmarks with these fields are destroyed.
. .
.feature org.openzfs chacha20_poly1305 no encryption extensible_dataset
This feature enables the use of the ChaCha20-Poly1305 cipher suite for encrypted
datasets.
On systems lacking hardware-accelerated AES (many non-x86 boards), this suite
will usually offer better performance than AES suites without compromising
security.
.Pp
This feature becomes
.Sy active
when an encrypted dataset is created with
.Nm encryption Ns = Ns Sy chacha20-poly1305
and will be returned to the
.Sy enabled
state when all datasets that use this feature are destroyed.
.
.feature org.openzfs device_rebuild yes .feature org.openzfs device_rebuild yes
This feature enables the ability for the This feature enables the ability for the
.Nm zpool Cm attach .Nm zpool Cm attach

View File

@ -120,8 +120,10 @@ ICP_OBJS := \
core/kcf_prov_tabs.o \ core/kcf_prov_tabs.o \
core/kcf_sched.o \ core/kcf_sched.o \
illumos-crypto.o \ illumos-crypto.o \
monocypher.o \
io/aes.o \ io/aes.o \
io/sha2_mod.o \ io/sha2_mod.o \
io/chapoly.o \
spi/kcf_spi.o spi/kcf_spi.o
ICP_OBJS_X86_64 := \ ICP_OBJS_X86_64 := \

View File

@ -109,6 +109,7 @@ icp_fini(void)
{ {
sha2_mod_fini(); sha2_mod_fini();
aes_mod_fini(); aes_mod_fini();
chapoly_mod_fini();
kcf_sched_destroy(); kcf_sched_destroy();
kcf_prov_tab_destroy(); kcf_prov_tab_destroy();
kcf_destroy_mech_tabs(); kcf_destroy_mech_tabs();
@ -131,6 +132,7 @@ icp_init(void)
kcf_sched_init(); kcf_sched_init();
/* initialize algorithms */ /* initialize algorithms */
chapoly_mod_init();
aes_mod_init(); aes_mod_init();
sha2_mod_init(); sha2_mod_init();

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2017-2019, Loup Vaillant
* All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Monocypher 4.0.2 (Poly1305, Chacha20, and supporting utilities)
* adapted for OpenZFS by Rob Norris <robn@despairlabs.com>
*/
/*
* Note: this follows the Monocypher style rather than the OpenZFS style to
* keep the diff to the bare minimum. This is important for making it easy to
* compare the two and confirm that they are in fact the same. The diff should
* be almost entirely in deleted lines.
*/
#ifndef MONOCYPHER_H
#define MONOCYPHER_H
#include <sys/types.h>
// Constant time comparisons
// -------------------------
// Return 0 if a and b are equal, -1 otherwise
int crypto_verify16(const uint8_t a[16], const uint8_t b[16]);
// Erase sensitive data
// --------------------
void crypto_wipe(void *secret, size_t size);
// Chacha20
// --------
// Unauthenticated stream cipher.
// Don't forget to add authentication.
uint32_t crypto_chacha20_ietf(uint8_t *cipher_text,
const uint8_t *plain_text,
size_t text_size,
const uint8_t key[32],
const uint8_t nonce[12],
uint32_t ctr);
// Poly 1305
// ---------
// This is a *one time* authenticator.
// Disclosing the mac reveals the key.
// Incremental interface
typedef struct {
// Do not rely on the size or contents of this type,
// for they may change without notice.
uint8_t c[16]; // chunk of the message
size_t c_idx; // How many bytes are there in the chunk.
uint32_t r [4]; // constant multiplier (from the secret key)
uint32_t pad[4]; // random number added at the end (from the secret key)
uint32_t h [5]; // accumulated hash
} crypto_poly1305_ctx;
void crypto_poly1305_init (crypto_poly1305_ctx *ctx, const uint8_t key[32]);
void crypto_poly1305_update(crypto_poly1305_ctx *ctx,
const uint8_t *message, size_t message_size);
void crypto_poly1305_final (crypto_poly1305_ctx *ctx, uint8_t mac[16]);
#endif /* MONOCYPHER_H */

492
module/icp/io/chapoly.c Normal file
View File

@ -0,0 +1,492 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or https://opensource.org/licenses/CDDL-1.0.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2024, Rob Norris <robn@despairlabs.com>
*/
/*
* ChaCha20-Poly1305 (RFC 8439) provider
*/
#include <sys/zfs_context.h>
#include <sys/crypto/common.h>
#include <sys/crypto/impl.h>
#include <sys/crypto/icp.h>
#include <monocypher.h>
/*
* convenient constants for readability. you can't change these; they're fixed
* to match defines and buffer sizes elsewhere.
*/
#define CP_BLOCK_SIZE (64)
#define CP_KEY_SIZE (32)
#define CP_MAC_SIZE (16)
#define CP_IV_SIZE (12)
typedef struct {
/* pointers back to the callers key and iv */
const uint8_t *key;
const uint8_t *iv;
/* poly1305 mac state */
crypto_poly1305_ctx poly;
/* counter value for next block */
uint32_t counter;
/* cipher output buffer and working space */
uint8_t temp[CP_BLOCK_SIZE];
/* bytes waiting for a complete block before they can be encrypted */
uint8_t pending[CP_BLOCK_SIZE];
size_t npending;
/* decrypt; data bytes remaining */
size_t datalen;
/* decrypt; pointer to pre-auth holding buffer */
/* (extra allocation past end of chapoly_ctx) */
uint8_t *unauthp;
} chapoly_ctx;
/* a bunch of zeroes for padding the poly1305 sequence */
static const uint8_t zero_pad[16] = {0};
static void
chapoly_init(chapoly_ctx *cpctx, const crypto_key_t *key, const uint8_t *iv)
{
cpctx->key = (const uint8_t *) key->ck_data;
cpctx->iv = (const uint8_t *) iv;
/* create the poly1305 key from the chacha block 0 keystream */
cpctx->counter = crypto_chacha20_ietf(
cpctx->temp, NULL, CP_KEY_SIZE,
cpctx->key, cpctx->iv, 0);
/* and intialise the context */
crypto_poly1305_init(&cpctx->poly, cpctx->temp);
}
static int
chapoly_encrypt_contiguous_blocks(
void *_cpctx, char *data, size_t length, crypto_data_t *out)
{
chapoly_ctx *cpctx = (chapoly_ctx *) _cpctx;
uint8_t *datap = (uint8_t *)data;
size_t nremaining = length;
size_t need;
int rv;
/* if there's anything in the pending buffer, try to empty it */
if (cpctx->npending > 0) {
/* take no more than we need to fill the temp buffer */
/* (one block), otherwise whatever is left */
need = nremaining > CP_BLOCK_SIZE - cpctx->npending ?
CP_BLOCK_SIZE - cpctx->npending : nremaining;
/* try fill that buffer */
memcpy(cpctx->pending + cpctx->npending, datap, need);
datap += need;
nremaining -= need;
cpctx->npending += need;
/* if we consumed everything and there's still not a full */
/* block then we've done all we can for now */
if (cpctx->npending < CP_BLOCK_SIZE) {
ASSERT0(nremaining);
return (CRYPTO_SUCCESS);
}
/* full block pending, process it */
cpctx->counter = crypto_chacha20_ietf(
cpctx->temp, cpctx->pending, CP_BLOCK_SIZE,
cpctx->key, cpctx->iv, cpctx->counter);
/* copy it to the output buffers */
rv = crypto_put_output_data(cpctx->temp, out, CP_BLOCK_SIZE);
if (rv != CRYPTO_SUCCESS)
return (rv);
/* update offset */
out->cd_offset += CP_BLOCK_SIZE;
/* update the mac */
crypto_poly1305_update(
&cpctx->poly, cpctx->temp, CP_BLOCK_SIZE);
/* pending buffer now drained */
cpctx->npending = 0;
}
/* process as many complete blocks as we can */
while (nremaining >= CP_BLOCK_SIZE) {
/* process one block */
cpctx->counter = crypto_chacha20_ietf(
cpctx->temp, datap, CP_BLOCK_SIZE,
cpctx->key, cpctx->iv, cpctx->counter);
/* copy it to the output buffers */
rv = crypto_put_output_data(cpctx->temp, out, CP_BLOCK_SIZE);
if (rv != CRYPTO_SUCCESS)
return (rv);
/* update offset */
out->cd_offset += CP_BLOCK_SIZE;
/* update the mac */
crypto_poly1305_update(
&cpctx->poly, cpctx->temp, CP_BLOCK_SIZE);
/* done a block */
datap += CP_BLOCK_SIZE;
nremaining -= CP_BLOCK_SIZE;
}
/* buffer anything left over for next time */
if (nremaining > 0) {
ASSERT3U(nremaining, <, CP_BLOCK_SIZE);
memcpy(cpctx->pending, datap, nremaining);
cpctx->npending = nremaining;
}
return (CRYPTO_SUCCESS);
}
static int
chapoly_encrypt_atomic(crypto_mechanism_t *mechanism,
crypto_key_t *key, crypto_data_t *plaintext, crypto_data_t *ciphertext,
crypto_spi_ctx_template_t __attribute__((unused)) template)
{
int rv;
/* We don't actually do GCM here, its just the default parameter */
/* option in zio_do_crypt_uio and has everything we need, so its */
/* easier to just take that instead of making our own thing. */
const CK_AES_GCM_PARAMS *gcmp =
(CK_AES_GCM_PARAMS *) mechanism->cm_param;
const uint8_t *iv = gcmp->pIv;
/* chacha20 invariants */
ASSERT3U(CRYPTO_BITS2BYTES(key->ck_length), ==, CP_KEY_SIZE);
ASSERT3U(gcmp->ulIvLen, ==, CP_IV_SIZE);
chapoly_ctx *cpctx = kmem_alloc(sizeof (chapoly_ctx), KM_SLEEP);
if (cpctx == NULL)
return (CRYPTO_HOST_MEMORY);
memset(cpctx, 0, sizeof (chapoly_ctx));
chapoly_init(cpctx, key, iv);
/* mix additional data into the mac */
crypto_poly1305_update(&cpctx->poly, gcmp->pAAD, gcmp->ulAADLen);
crypto_poly1305_update(
&cpctx->poly, zero_pad, (~(gcmp->ulAADLen) + 1) & 0xf);
off_t saved_offset = ciphertext->cd_offset;
size_t saved_length = ciphertext->cd_length;
switch (plaintext->cd_format) {
case CRYPTO_DATA_RAW:
rv = crypto_update_iov(
cpctx, plaintext, ciphertext,
chapoly_encrypt_contiguous_blocks);
break;
case CRYPTO_DATA_UIO:
rv = crypto_update_uio(
cpctx, plaintext, ciphertext,
chapoly_encrypt_contiguous_blocks);
break;
default:
rv = CRYPTO_ARGUMENTS_BAD;
}
if (rv == CRYPTO_SUCCESS) {
/* process and emit anything in the pending buffer */
if (cpctx->npending > 0) {
crypto_chacha20_ietf(
cpctx->temp, cpctx->pending, cpctx->npending,
cpctx->key, cpctx->iv, cpctx->counter);
/* write the last bit of the ciphertext */
rv = crypto_put_output_data(
cpctx->temp, ciphertext, cpctx->npending);
if (rv != CRYPTO_SUCCESS)
goto out;
ciphertext->cd_offset += cpctx->npending;
/* and update the mac */
crypto_poly1305_update(
&cpctx->poly, cpctx->temp, cpctx->npending);
}
/* finish the mac */
uint64_t sizes[2] = {
LE_64(gcmp->ulAADLen), LE_64(plaintext->cd_length)
};
crypto_poly1305_update(
&cpctx->poly, zero_pad,
(~(plaintext->cd_length) + 1) & 0xf);
crypto_poly1305_update(&cpctx->poly, (uint8_t *)sizes, 16);
crypto_poly1305_final(&cpctx->poly, cpctx->temp);
/* and write it out */
rv = crypto_put_output_data(
cpctx->temp, ciphertext, CP_MAC_SIZE);
if (rv != CRYPTO_SUCCESS)
goto out;
ciphertext->cd_offset += CP_MAC_SIZE;
ciphertext->cd_length = ciphertext->cd_offset - saved_offset;
}
else
ciphertext->cd_length = saved_length;
ciphertext->cd_offset = saved_offset;
out:
crypto_wipe(cpctx, sizeof (chapoly_ctx));
kmem_free(cpctx, sizeof (chapoly_ctx));
return (rv);
}
static int
chapoly_decrypt_contiguous_blocks(
void *_cpctx, char *data, size_t length,
crypto_data_t __attribute__((unused)) *out)
{
chapoly_ctx *cpctx = (chapoly_ctx *) _cpctx;
size_t need;
if (cpctx->datalen > 0) {
/* these are data bytes */
/* don't take more than we need; the mac might be on the end */
need = length > cpctx->datalen ? cpctx->datalen : length;
/* copy the ciphertext to a buffer we made for it */
memcpy(cpctx->unauthp, data, need);
cpctx->unauthp += need;
cpctx->datalen -= need;
/* update the mac */
crypto_poly1305_update(&cpctx->poly, (uint8_t *)data, need);
/* update how much we're still expecting */
length -= need;
data += need;
/* if we consumed the whole buffer, we're done */
if (length == 0)
return (CRYPTO_SUCCESS);
}
/* these are mac bytes */
/* assume that the mac always arrives in a single block, not split */
/* over blocks. this is true for OpenZFS at least */
if (length != CP_MAC_SIZE)
return (CRYPTO_DATA_LEN_RANGE);
/* leave the incoming mac in the temp buffer */
memcpy(cpctx->temp, data, 16);
return (CRYPTO_SUCCESS);
}
static int
chapoly_decrypt_finish(
chapoly_ctx *cpctx, size_t length, crypto_data_t *out)
{
uint8_t *datap = (uint8_t *)cpctx + sizeof (chapoly_ctx);
size_t nremaining = length;
size_t need;
int rv;
while (nremaining > 0) {
/* take no more than we need to fill the temp buffer */
/* (one block), otherwise whatever is left */
need = nremaining > CP_BLOCK_SIZE ? CP_BLOCK_SIZE : nremaining;
/* process a block */
cpctx->counter = crypto_chacha20_ietf(
cpctx->temp, datap, need,
cpctx->key, cpctx->iv, cpctx->counter);
/* copy it into the output buffers */
rv = crypto_put_output_data(cpctx->temp, out, need);
if (rv != CRYPTO_SUCCESS)
return (rv);
/* update offset */
out->cd_offset += need;
/* update remaining */
nremaining -= need;
datap += need;
}
return (CRYPTO_SUCCESS);
}
static int
chapoly_decrypt_atomic(crypto_mechanism_t *mechanism,
crypto_key_t *key, crypto_data_t *ciphertext, crypto_data_t *plaintext,
crypto_spi_ctx_template_t __attribute__((unused)) template)
{
int rv;
/* We don't actually do GCM here, its just the default parameter */
/* option in zio_do_crypt_uio and has everything we need, so its */
/* easier to just take that instead of making our own thing. */
const CK_AES_GCM_PARAMS *gcmp =
(CK_AES_GCM_PARAMS*) mechanism->cm_param;
const uint8_t *iv = gcmp->pIv;
/* chacha20 invariants */
ASSERT3U(CRYPTO_BITS2BYTES(key->ck_length), ==, CP_KEY_SIZE);
ASSERT3U(gcmp->ulIvLen, ==, CP_IV_SIZE);
ASSERT3U(CRYPTO_BITS2BYTES(gcmp->ulTagBits), ==, CP_MAC_SIZE);
size_t datalen = ciphertext->cd_length - CP_MAC_SIZE;
chapoly_ctx *cpctx = vmem_alloc(
sizeof (chapoly_ctx) + datalen, KM_SLEEP);
if (cpctx == NULL)
return (CRYPTO_HOST_MEMORY);
memset(cpctx, 0, sizeof (chapoly_ctx) + datalen);
chapoly_init(cpctx, key, iv);
/* mix additional data into the mac */
crypto_poly1305_update(&cpctx->poly, gcmp->pAAD, gcmp->ulAADLen);
crypto_poly1305_update(
&cpctx->poly, zero_pad, (~(gcmp->ulAADLen) + 1) & 0xf);
cpctx->datalen = datalen;
cpctx->unauthp = (uint8_t *)cpctx + sizeof (chapoly_ctx);
off_t saved_offset = plaintext->cd_offset;
size_t saved_length = plaintext->cd_length;
switch (ciphertext->cd_format) {
case CRYPTO_DATA_RAW:
rv = crypto_update_iov(
cpctx, ciphertext, plaintext,
chapoly_decrypt_contiguous_blocks);
break;
case CRYPTO_DATA_UIO:
rv = crypto_update_uio(
cpctx, ciphertext, plaintext,
chapoly_decrypt_contiguous_blocks);
break;
default:
rv = CRYPTO_ARGUMENTS_BAD;
}
if (rv == CRYPTO_SUCCESS) {
/* finish the mac. the incoming mac is at that start of the */
/* temp buffer, so we'll write the computed one after it */
uint64_t sizes[2] = {
LE_64(gcmp->ulAADLen), LE_64(datalen)
};
crypto_poly1305_update(
&cpctx->poly, zero_pad, (~(datalen) + 1) & 0xf);
crypto_poly1305_update(&cpctx->poly, (uint8_t *)sizes, 16);
crypto_poly1305_final(&cpctx->poly, cpctx->temp + CP_MAC_SIZE);
/* now compare them */
if (crypto_verify16(
cpctx->temp, cpctx->temp + CP_MAC_SIZE) != 0)
rv = CRYPTO_INVALID_MAC;
}
/* mac checks out; we're ready to decrypt */
if (rv == CRYPTO_SUCCESS)
/* mac has been checked, now we can decrypt */
rv = chapoly_decrypt_finish(cpctx, datalen, plaintext);
if (rv == CRYPTO_SUCCESS)
plaintext->cd_length = plaintext->cd_offset - saved_offset;
else
plaintext->cd_length = saved_length;
plaintext->cd_offset = saved_offset;
crypto_wipe(cpctx, sizeof (chapoly_ctx) + datalen);
vmem_free(cpctx, sizeof (chapoly_ctx) + datalen);
return (rv);
}
static const crypto_mech_info_t chapoly_mech_info_tab[] = {
{SUN_CKM_CHACHA20_POLY1305, 0,
CRYPTO_FG_ENCRYPT_ATOMIC | CRYPTO_FG_DECRYPT_ATOMIC },
};
static const crypto_cipher_ops_t chapoly_cipher_ops = {
.encrypt_atomic = chapoly_encrypt_atomic,
.decrypt_atomic = chapoly_decrypt_atomic
};
static const crypto_ops_t chapoly_crypto_ops = {
.co_cipher_ops = &chapoly_cipher_ops,
.co_mac_ops = NULL,
.co_ctx_ops = NULL,
};
static const crypto_provider_info_t chapoly_prov_info = {
"Chacha20-Poly1305 Software Provider",
&chapoly_crypto_ops,
sizeof (chapoly_mech_info_tab) / sizeof (crypto_mech_info_t),
chapoly_mech_info_tab
};
static crypto_kcf_provider_handle_t chapoly_prov_handle = 0;
int
chapoly_mod_init(void)
{
/* Register with KCF. If the registration fails, remove the module. */
if (crypto_register_provider(&chapoly_prov_info, &chapoly_prov_handle))
return (EACCES);
return (0);
}
int
chapoly_mod_fini(void)
{
/* Unregister from KCF if module is registered */
if (chapoly_prov_handle != 0) {
if (crypto_unregister_provider(chapoly_prov_handle))
return (EBUSY);
chapoly_prov_handle = 0;
}
return (0);
}

377
module/icp/monocypher.c Normal file
View File

@ -0,0 +1,377 @@
/*
* Copyright (c) 2017-2019, Loup Vaillant
* All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Monocypher 4.0.2 (Poly1305, Chacha20, and supporting utilities)
* adapted for OpenZFS by Rob Norris <robn@despairlabs.com>
*/
/*
* Note: this follows the Monocypher style rather than the OpenZFS style to
* keep the diff to the bare minimum. This is important for making it easy to
* compare the two and confirm that they are in fact the same. The diff should
* be almost entirely in deleted lines.
*/
#include "monocypher.h"
/////////////////
/// Utilities ///
/////////////////
#define FOR_T(type, i, start, end) for (type i = (start); i < (end); i++)
#define FOR(i, start, end) FOR_T(size_t, i, start, end)
#define ZERO(buf, size) FOR(_i_, 0, size) (buf)[_i_] = 0
#define WIPE_CTX(ctx) crypto_wipe(ctx , sizeof(*(ctx)))
#define WIPE_BUFFER(buffer) crypto_wipe(buffer, sizeof(buffer))
/*
* OpenZFS: userspace libicp build on Linux will already have MIN/MAX defined
* through sys/types.h -> sys/param.h. Undefine them and let Monocypher use its
* own, in case they change in some important way in the future.
*/
#undef MIN
#undef MAX
#define MIN(a, b) ((a) <= (b) ? (a) : (b))
#define MAX(a, b) ((a) >= (b) ? (a) : (b))
typedef int8_t i8;
typedef uint8_t u8;
typedef int16_t i16;
typedef uint32_t u32;
typedef int32_t i32;
typedef int64_t i64;
typedef uint64_t u64;
static const u8 zero[128] = {0};
// returns the smallest positive integer y such that
// (x + y) % pow_2 == 0
// Basically, y is the "gap" missing to align x.
// Only works when pow_2 is a power of 2.
// Note: we use ~x+1 instead of -x to avoid compiler warnings
static size_t gap(size_t x, size_t pow_2)
{
return (~x + 1) & (pow_2 - 1);
}
static u32 load32_le(const u8 s[4])
{
return
((u32)s[0] << 0) |
((u32)s[1] << 8) |
((u32)s[2] << 16) |
((u32)s[3] << 24);
}
static u64 load64_le(const u8 s[8])
{
return load32_le(s) | ((u64)load32_le(s+4) << 32);
}
static void store32_le(u8 out[4], u32 in)
{
out[0] = in & 0xff;
out[1] = (in >> 8) & 0xff;
out[2] = (in >> 16) & 0xff;
out[3] = (in >> 24) & 0xff;
}
static void load32_le_buf (u32 *dst, const u8 *src, size_t size) {
FOR(i, 0, size) { dst[i] = load32_le(src + i*4); }
}
static u32 rotl32(u32 x, u32 n) { return (x << n) ^ (x >> (32 - n)); }
static int neq0(u64 diff)
{
// constant time comparison to zero
// return diff != 0 ? -1 : 0
u64 half = (diff >> 32) | ((u32)diff);
return (1 & ((half - 1) >> 32)) - 1;
}
static u64 x16(const u8 a[16], const u8 b[16])
{
return (load64_le(a + 0) ^ load64_le(b + 0))
| (load64_le(a + 8) ^ load64_le(b + 8));
}
int crypto_verify16(const u8 a[16], const u8 b[16]){ return neq0(x16(a, b)); }
void crypto_wipe(void *secret, size_t size)
{
volatile u8 *v_secret = (u8*)secret;
ZERO(v_secret, size);
}
/////////////////
/// Chacha 20 ///
/////////////////
#define QUARTERROUND(a, b, c, d) \
a += b; d = rotl32(d ^ a, 16); \
c += d; b = rotl32(b ^ c, 12); \
a += b; d = rotl32(d ^ a, 8); \
c += d; b = rotl32(b ^ c, 7)
static void chacha20_rounds(u32 out[16], const u32 in[16])
{
// The temporary variables make Chacha20 10% faster.
u32 t0 = in[ 0]; u32 t1 = in[ 1]; u32 t2 = in[ 2]; u32 t3 = in[ 3];
u32 t4 = in[ 4]; u32 t5 = in[ 5]; u32 t6 = in[ 6]; u32 t7 = in[ 7];
u32 t8 = in[ 8]; u32 t9 = in[ 9]; u32 t10 = in[10]; u32 t11 = in[11];
u32 t12 = in[12]; u32 t13 = in[13]; u32 t14 = in[14]; u32 t15 = in[15];
FOR (i, 0, 10) { // 20 rounds, 2 rounds per loop.
QUARTERROUND(t0, t4, t8 , t12); // column 0
QUARTERROUND(t1, t5, t9 , t13); // column 1
QUARTERROUND(t2, t6, t10, t14); // column 2
QUARTERROUND(t3, t7, t11, t15); // column 3
QUARTERROUND(t0, t5, t10, t15); // diagonal 0
QUARTERROUND(t1, t6, t11, t12); // diagonal 1
QUARTERROUND(t2, t7, t8 , t13); // diagonal 2
QUARTERROUND(t3, t4, t9 , t14); // diagonal 3
}
out[ 0] = t0; out[ 1] = t1; out[ 2] = t2; out[ 3] = t3;
out[ 4] = t4; out[ 5] = t5; out[ 6] = t6; out[ 7] = t7;
out[ 8] = t8; out[ 9] = t9; out[10] = t10; out[11] = t11;
out[12] = t12; out[13] = t13; out[14] = t14; out[15] = t15;
}
static const u8 *chacha20_constant = (const u8*)"expand 32-byte k"; // 16 bytes
static u64 crypto_chacha20_djb(u8 *cipher_text, const u8 *plain_text,
size_t text_size, const u8 key[32], const u8 nonce[8],
u64 ctr)
{
u32 input[16];
load32_le_buf(input , chacha20_constant, 4);
load32_le_buf(input + 4, key , 8);
load32_le_buf(input + 14, nonce , 2);
input[12] = (u32) ctr;
input[13] = (u32)(ctr >> 32);
// Whole blocks
u32 pool[16];
size_t nb_blocks = text_size >> 6;
FOR (i, 0, nb_blocks) {
chacha20_rounds(pool, input);
if (plain_text != 0) {
FOR (j, 0, 16) {
u32 p = pool[j] + input[j];
store32_le(cipher_text, p ^ load32_le(plain_text));
cipher_text += 4;
plain_text += 4;
}
} else {
FOR (j, 0, 16) {
u32 p = pool[j] + input[j];
store32_le(cipher_text, p);
cipher_text += 4;
}
}
input[12]++;
if (input[12] == 0) {
input[13]++;
}
}
text_size &= 63;
// Last (incomplete) block
if (text_size > 0) {
if (plain_text == 0) {
plain_text = zero;
}
chacha20_rounds(pool, input);
u8 tmp[64];
FOR (i, 0, 16) {
store32_le(tmp + i*4, pool[i] + input[i]);
}
FOR (i, 0, text_size) {
cipher_text[i] = tmp[i] ^ plain_text[i];
}
WIPE_BUFFER(tmp);
}
ctr = input[12] + ((u64)input[13] << 32) + (text_size > 0);
WIPE_BUFFER(pool);
WIPE_BUFFER(input);
return ctr;
}
u32 crypto_chacha20_ietf(u8 *cipher_text, const u8 *plain_text,
size_t text_size,
const u8 key[32], const u8 nonce[12], u32 ctr)
{
u64 big_ctr = ctr + ((u64)load32_le(nonce) << 32);
return (u32)crypto_chacha20_djb(cipher_text, plain_text, text_size,
key, nonce + 4, big_ctr);
}
/////////////////
/// Poly 1305 ///
/////////////////
// h = (h + c) * r
// preconditions:
// ctx->h <= 4_ffffffff_ffffffff_ffffffff_ffffffff
// ctx->r <= 0ffffffc_0ffffffc_0ffffffc_0fffffff
// end <= 1
// Postcondition:
// ctx->h <= 4_ffffffff_ffffffff_ffffffff_ffffffff
static void poly_blocks(crypto_poly1305_ctx *ctx, const u8 *in,
size_t nb_blocks, unsigned end)
{
// Local all the things!
const u32 r0 = ctx->r[0];
const u32 r1 = ctx->r[1];
const u32 r2 = ctx->r[2];
const u32 r3 = ctx->r[3];
const u32 rr0 = (r0 >> 2) * 5; // lose 2 bits...
const u32 rr1 = (r1 >> 2) + r1; // rr1 == (r1 >> 2) * 5
const u32 rr2 = (r2 >> 2) + r2; // rr1 == (r2 >> 2) * 5
const u32 rr3 = (r3 >> 2) + r3; // rr1 == (r3 >> 2) * 5
const u32 rr4 = r0 & 3; // ...recover 2 bits
u32 h0 = ctx->h[0];
u32 h1 = ctx->h[1];
u32 h2 = ctx->h[2];
u32 h3 = ctx->h[3];
u32 h4 = ctx->h[4];
FOR (i, 0, nb_blocks) {
// h + c, without carry propagation
const u64 s0 = (u64)h0 + load32_le(in); in += 4;
const u64 s1 = (u64)h1 + load32_le(in); in += 4;
const u64 s2 = (u64)h2 + load32_le(in); in += 4;
const u64 s3 = (u64)h3 + load32_le(in); in += 4;
const u32 s4 = h4 + end;
// (h + c) * r, without carry propagation
const u64 x0 = s0*r0+ s1*rr3+ s2*rr2+ s3*rr1+ s4*rr0;
const u64 x1 = s0*r1+ s1*r0 + s2*rr3+ s3*rr2+ s4*rr1;
const u64 x2 = s0*r2+ s1*r1 + s2*r0 + s3*rr3+ s4*rr2;
const u64 x3 = s0*r3+ s1*r2 + s2*r1 + s3*r0 + s4*rr3;
const u32 x4 = s4*rr4;
// partial reduction modulo 2^130 - 5
const u32 u5 = x4 + (x3 >> 32); // u5 <= 7ffffff5
const u64 u0 = (u5 >> 2) * 5 + (x0 & 0xffffffff);
const u64 u1 = (u0 >> 32) + (x1 & 0xffffffff) + (x0 >> 32);
const u64 u2 = (u1 >> 32) + (x2 & 0xffffffff) + (x1 >> 32);
const u64 u3 = (u2 >> 32) + (x3 & 0xffffffff) + (x2 >> 32);
const u32 u4 = (u3 >> 32) + (u5 & 3); // u4 <= 4
// Update the hash
h0 = u0 & 0xffffffff;
h1 = u1 & 0xffffffff;
h2 = u2 & 0xffffffff;
h3 = u3 & 0xffffffff;
h4 = u4;
}
ctx->h[0] = h0;
ctx->h[1] = h1;
ctx->h[2] = h2;
ctx->h[3] = h3;
ctx->h[4] = h4;
}
void crypto_poly1305_init(crypto_poly1305_ctx *ctx, const u8 key[32])
{
ZERO(ctx->h, 5); // Initial hash is zero
ctx->c_idx = 0;
// load r and pad (r has some of its bits cleared)
load32_le_buf(ctx->r , key , 4);
load32_le_buf(ctx->pad, key+16, 4);
FOR (i, 0, 1) { ctx->r[i] &= 0x0fffffff; }
FOR (i, 1, 4) { ctx->r[i] &= 0x0ffffffc; }
}
void crypto_poly1305_update(crypto_poly1305_ctx *ctx,
const u8 *message, size_t message_size)
{
// Avoid undefined NULL pointer increments with empty messages
if (message_size == 0) {
return;
}
// Align ourselves with block boundaries
size_t aligned = MIN(gap(ctx->c_idx, 16), message_size);
FOR (i, 0, aligned) {
ctx->c[ctx->c_idx] = *message;
ctx->c_idx++;
message++;
message_size--;
}
// If block is complete, process it
if (ctx->c_idx == 16) {
poly_blocks(ctx, ctx->c, 1, 1);
ctx->c_idx = 0;
}
// Process the message block by block
size_t nb_blocks = message_size >> 4;
poly_blocks(ctx, message, nb_blocks, 1);
message += nb_blocks << 4;
message_size &= 15;
// remaining bytes (we never complete a block here)
FOR (i, 0, message_size) {
ctx->c[ctx->c_idx] = message[i];
ctx->c_idx++;
}
}
void crypto_poly1305_final(crypto_poly1305_ctx *ctx, u8 mac[16])
{
// Process the last block (if any)
// We move the final 1 according to remaining input length
// (this will add less than 2^130 to the last input block)
if (ctx->c_idx != 0) {
ZERO(ctx->c + ctx->c_idx, 16 - ctx->c_idx);
ctx->c[ctx->c_idx] = 1;
poly_blocks(ctx, ctx->c, 1, 0);
}
// check if we should subtract 2^130-5 by performing the
// corresponding carry propagation.
u64 c = 5;
FOR (i, 0, 4) {
c += ctx->h[i];
c >>= 32;
}
c += ctx->h[4];
c = (c >> 2) * 5; // shift the carry back to the beginning
// c now indicates how many times we should subtract 2^130-5 (0 or 1)
FOR (i, 0, 4) {
c += (u64)ctx->h[i] + ctx->pad[i];
store32_le(mac + i*4, (u32)c);
c = c >> 32;
}
WIPE_CTX(ctx);
}

View File

@ -294,6 +294,17 @@ freebsd_crypt_newsession(freebsd_crypt_session_t *sessp,
break; break;
} }
break; break;
case ZC_TYPE_CHACHA20_POLY1305:
csp.csp_cipher_alg = CRYPTO_CHACHA20_POLY1305;
csp.csp_ivlen = CHACHA20_POLY1305_IV_LEN;
switch (key->ck_length/8) {
case CHACHA20_POLY1305_KEY:
break;
default:
error = EINVAL;
goto bad;
}
break;
default: default:
error = ENOTSUP; error = ENOTSUP;
goto bad; goto bad;

View File

@ -194,15 +194,26 @@ typedef struct blkptr_auth_buf {
} blkptr_auth_buf_t; } blkptr_auth_buf_t;
const zio_crypt_info_t zio_crypt_table[ZIO_CRYPT_FUNCTIONS] = { const zio_crypt_info_t zio_crypt_table[ZIO_CRYPT_FUNCTIONS] = {
{"", ZC_TYPE_NONE, 0, "inherit"}, {"", ZC_TYPE_NONE,
{"", ZC_TYPE_NONE, 0, "on"}, 0, "inherit"},
{"", ZC_TYPE_NONE, 0, "off"}, {"", ZC_TYPE_NONE,
{SUN_CKM_AES_CCM, ZC_TYPE_CCM, 16, "aes-128-ccm"}, 0, "on"},
{SUN_CKM_AES_CCM, ZC_TYPE_CCM, 24, "aes-192-ccm"}, {"", ZC_TYPE_NONE,
{SUN_CKM_AES_CCM, ZC_TYPE_CCM, 32, "aes-256-ccm"}, 0, "off"},
{SUN_CKM_AES_GCM, ZC_TYPE_GCM, 16, "aes-128-gcm"}, {SUN_CKM_AES_CCM, ZC_TYPE_CCM,
{SUN_CKM_AES_GCM, ZC_TYPE_GCM, 24, "aes-192-gcm"}, 16, "aes-128-ccm"},
{SUN_CKM_AES_GCM, ZC_TYPE_GCM, 32, "aes-256-gcm"} {SUN_CKM_AES_CCM, ZC_TYPE_CCM,
24, "aes-192-ccm"},
{SUN_CKM_AES_CCM, ZC_TYPE_CCM,
32, "aes-256-ccm"},
{SUN_CKM_AES_GCM, ZC_TYPE_GCM,
16, "aes-128-gcm"},
{SUN_CKM_AES_GCM, ZC_TYPE_GCM,
24, "aes-192-gcm"},
{SUN_CKM_AES_GCM, ZC_TYPE_GCM,
32, "aes-256-gcm"},
{SUN_CKM_CHACHA20_POLY1305, ZC_TYPE_CHACHA20_POLY1305,
32, "chacha20-poly1305"},
}; };
static void static void
@ -238,7 +249,8 @@ zio_crypt_key_init(uint64_t crypt, zio_crypt_key_t *key)
ci = &zio_crypt_table[crypt]; ci = &zio_crypt_table[crypt];
if (ci->ci_crypt_type != ZC_TYPE_GCM && if (ci->ci_crypt_type != ZC_TYPE_GCM &&
ci->ci_crypt_type != ZC_TYPE_CCM) ci->ci_crypt_type != ZC_TYPE_CCM &&
ci->ci_crypt_type != ZC_TYPE_CHACHA20_POLY1305)
return (ENOTSUP); return (ENOTSUP);
keydata_len = zio_crypt_table[crypt].ci_keylen; keydata_len = zio_crypt_table[crypt].ci_keylen;
@ -278,7 +290,8 @@ zio_crypt_key_init(uint64_t crypt, zio_crypt_key_t *key)
ci = &zio_crypt_table[crypt]; ci = &zio_crypt_table[crypt];
if (ci->ci_crypt_type != ZC_TYPE_GCM && if (ci->ci_crypt_type != ZC_TYPE_GCM &&
ci->ci_crypt_type != ZC_TYPE_CCM) ci->ci_crypt_type != ZC_TYPE_CCM &&
ci->ci_crypt_type != ZC_TYPE_CHACHA20_POLY1305)
return (ENOTSUP); return (ENOTSUP);
ret = freebsd_crypt_newsession(&key->zk_session, ci, ret = freebsd_crypt_newsession(&key->zk_session, ci,
@ -400,7 +413,8 @@ zio_do_crypt_uio_opencrypto(boolean_t encrypt, freebsd_crypt_session_t *sess,
{ {
const zio_crypt_info_t *ci = &zio_crypt_table[crypt]; const zio_crypt_info_t *ci = &zio_crypt_table[crypt];
if (ci->ci_crypt_type != ZC_TYPE_GCM && if (ci->ci_crypt_type != ZC_TYPE_GCM &&
ci->ci_crypt_type != ZC_TYPE_CCM) ci->ci_crypt_type != ZC_TYPE_CCM &&
ci->ci_crypt_type != ZC_TYPE_CHACHA20_POLY1305)
return (ENOTSUP); return (ENOTSUP);

View File

@ -195,15 +195,26 @@ typedef struct blkptr_auth_buf {
} blkptr_auth_buf_t; } blkptr_auth_buf_t;
const zio_crypt_info_t zio_crypt_table[ZIO_CRYPT_FUNCTIONS] = { const zio_crypt_info_t zio_crypt_table[ZIO_CRYPT_FUNCTIONS] = {
{"", ZC_TYPE_NONE, 0, "inherit"}, {"", ZC_TYPE_NONE,
{"", ZC_TYPE_NONE, 0, "on"}, 0, "inherit"},
{"", ZC_TYPE_NONE, 0, "off"}, {"", ZC_TYPE_NONE,
{SUN_CKM_AES_CCM, ZC_TYPE_CCM, 16, "aes-128-ccm"}, 0, "on"},
{SUN_CKM_AES_CCM, ZC_TYPE_CCM, 24, "aes-192-ccm"}, {"", ZC_TYPE_NONE,
{SUN_CKM_AES_CCM, ZC_TYPE_CCM, 32, "aes-256-ccm"}, 0, "off"},
{SUN_CKM_AES_GCM, ZC_TYPE_GCM, 16, "aes-128-gcm"}, {SUN_CKM_AES_CCM, ZC_TYPE_CCM,
{SUN_CKM_AES_GCM, ZC_TYPE_GCM, 24, "aes-192-gcm"}, 16, "aes-128-ccm"},
{SUN_CKM_AES_GCM, ZC_TYPE_GCM, 32, "aes-256-gcm"} {SUN_CKM_AES_CCM, ZC_TYPE_CCM,
24, "aes-192-ccm"},
{SUN_CKM_AES_CCM, ZC_TYPE_CCM,
32, "aes-256-ccm"},
{SUN_CKM_AES_GCM, ZC_TYPE_GCM,
16, "aes-128-gcm"},
{SUN_CKM_AES_GCM, ZC_TYPE_GCM,
24, "aes-192-gcm"},
{SUN_CKM_AES_GCM, ZC_TYPE_GCM,
32, "aes-256-gcm"},
{SUN_CKM_CHACHA20_POLY1305, ZC_TYPE_CHACHA20_POLY1305,
32, "chacha20-poly1305"},
}; };
void void

View File

@ -760,6 +760,19 @@ zpool_feature_init(void)
ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL, ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL,
sfeatures); sfeatures);
{
static const spa_feature_t chapoly_deps[] = {
SPA_FEATURE_EXTENSIBLE_DATASET,
SPA_FEATURE_ENCRYPTION,
SPA_FEATURE_NONE
};
zfeature_register(SPA_FEATURE_CHACHA20_POLY1305,
"com.despairlabs:chacha20_poly1305", "chacha20_poly1305",
"Chacha20-Poly1305 encryption suite.",
ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN,
chapoly_deps, sfeatures);
}
zfs_mod_list_supported_free(sfeatures); zfs_mod_list_supported_free(sfeatures);
} }

View File

@ -223,6 +223,7 @@ zfs_prop_init(void)
{ "aes-128-gcm", ZIO_CRYPT_AES_128_GCM }, { "aes-128-gcm", ZIO_CRYPT_AES_128_GCM },
{ "aes-192-gcm", ZIO_CRYPT_AES_192_GCM }, { "aes-192-gcm", ZIO_CRYPT_AES_192_GCM },
{ "aes-256-gcm", ZIO_CRYPT_AES_256_GCM }, { "aes-256-gcm", ZIO_CRYPT_AES_256_GCM },
{ "chacha20-poly1305", ZIO_CRYPT_CHACHA20_POLY1305 },
{ NULL } { NULL }
}; };
@ -552,8 +553,8 @@ zfs_prop_init(void)
zprop_register_index(ZFS_PROP_ENCRYPTION, "encryption", zprop_register_index(ZFS_PROP_ENCRYPTION, "encryption",
ZIO_CRYPT_DEFAULT, PROP_ONETIME, ZFS_TYPE_DATASET, ZIO_CRYPT_DEFAULT, PROP_ONETIME, ZFS_TYPE_DATASET,
"on | off | aes-128-ccm | aes-192-ccm | aes-256-ccm | " "on | off | aes-128-ccm | aes-192-ccm | aes-256-ccm | "
"aes-128-gcm | aes-192-gcm | aes-256-gcm", "ENCRYPTION", "aes-128-gcm | aes-192-gcm | aes-256-gcm | chacha20-poly1305",
crypto_table, sfeatures); "ENCRYPTION", crypto_table, sfeatures);
/* set once index (boolean) properties */ /* set once index (boolean) properties */
zprop_register_index(ZFS_PROP_UTF8ONLY, "utf8only", 0, PROP_ONETIME, zprop_register_index(ZFS_PROP_UTF8ONLY, "utf8only", 0, PROP_ONETIME,

View File

@ -1853,6 +1853,12 @@ dmu_objset_create_crypt_check(dsl_dir_t *parentdd, dsl_crypto_params_t *dcp,
return (SET_ERROR(EOPNOTSUPP)); return (SET_ERROR(EOPNOTSUPP));
} }
if (crypt == ZIO_CRYPT_CHACHA20_POLY1305 && parentdd != NULL &&
!spa_feature_is_enabled(parentdd->dd_pool->dp_spa,
SPA_FEATURE_CHACHA20_POLY1305)) {
return (SET_ERROR(EOPNOTSUPP));
}
/* handle inheritance */ /* handle inheritance */
if (dcp->cp_wkey == NULL) { if (dcp->cp_wkey == NULL) {
ASSERT3P(parentdd, !=, NULL); ASSERT3P(parentdd, !=, NULL);
@ -1971,6 +1977,9 @@ dsl_dataset_create_crypt_sync(uint64_t dsobj, dsl_dir_t *dd,
tx)); tx));
dsl_dataset_activate_feature(dsobj, SPA_FEATURE_ENCRYPTION, dsl_dataset_activate_feature(dsobj, SPA_FEATURE_ENCRYPTION,
(void *)B_TRUE, tx); (void *)B_TRUE, tx);
if (crypt == ZIO_CRYPT_CHACHA20_POLY1305)
dsl_dataset_activate_feature(dsobj,
SPA_FEATURE_CHACHA20_POLY1305, (void *)B_TRUE, tx);
/* /*
* If we inherited the wrapping key we release our reference now. * If we inherited the wrapping key we release our reference now.
@ -2191,6 +2200,11 @@ dsl_crypto_recv_raw_key_check(dsl_dataset_t *ds, nvlist_t *nvl, dmu_tx_t *tx)
if (intval >= ZIO_CRYPT_FUNCTIONS) if (intval >= ZIO_CRYPT_FUNCTIONS)
return (SET_ERROR(ZFS_ERR_CRYPTO_NOTSUP)); return (SET_ERROR(ZFS_ERR_CRYPTO_NOTSUP));
if (intval == ZIO_CRYPT_CHACHA20_POLY1305 &&
!spa_feature_is_enabled(ds->ds_dir->dd_pool->dp_spa,
SPA_FEATURE_CHACHA20_POLY1305))
return (SET_ERROR(EOPNOTSUPP));
ret = nvlist_lookup_uint64(nvl, DSL_CRYPTO_KEY_GUID, &intval); ret = nvlist_lookup_uint64(nvl, DSL_CRYPTO_KEY_GUID, &intval);
if (ret != 0) if (ret != 0)
return (SET_ERROR(EINVAL)); return (SET_ERROR(EINVAL));
@ -2310,6 +2324,13 @@ dsl_crypto_recv_raw_key_sync(dsl_dataset_t *ds, nvlist_t *nvl, dmu_tx_t *tx)
SPA_FEATURE_ENCRYPTION, (void *)B_TRUE, tx); SPA_FEATURE_ENCRYPTION, (void *)B_TRUE, tx);
ds->ds_feature[SPA_FEATURE_ENCRYPTION] = (void *)B_TRUE; ds->ds_feature[SPA_FEATURE_ENCRYPTION] = (void *)B_TRUE;
if (crypt == ZIO_CRYPT_CHACHA20_POLY1305) {
dsl_dataset_activate_feature(ds->ds_object,
SPA_FEATURE_CHACHA20_POLY1305, (void *)B_TRUE, tx);
ds->ds_feature[SPA_FEATURE_CHACHA20_POLY1305] =
(void *)B_TRUE;
}
/* save the dd_crypto_obj on disk */ /* save the dd_crypto_obj on disk */
VERIFY0(zap_add(mos, dd->dd_object, DD_FIELD_CRYPTO_KEY_OBJ, VERIFY0(zap_add(mos, dd->dd_object, DD_FIELD_CRYPTO_KEY_OBJ,
sizeof (uint64_t), 1, &dd->dd_crypto_obj, tx)); sizeof (uint64_t), 1, &dd->dd_crypto_obj, tx));

View File

@ -530,8 +530,12 @@ dsl_pool_create(spa_t *spa, nvlist_t *zplprops __attribute__((unused)),
spa_feature_create_zap_objects(spa, tx); spa_feature_create_zap_objects(spa, tx);
if (dcp != NULL && dcp->cp_crypt != ZIO_CRYPT_OFF && if (dcp != NULL && dcp->cp_crypt != ZIO_CRYPT_OFF &&
dcp->cp_crypt != ZIO_CRYPT_INHERIT) dcp->cp_crypt != ZIO_CRYPT_INHERIT) {
spa_feature_enable(spa, SPA_FEATURE_ENCRYPTION, tx); spa_feature_enable(spa, SPA_FEATURE_ENCRYPTION, tx);
if (dcp->cp_crypt == ZIO_CRYPT_CHACHA20_POLY1305)
spa_feature_enable(spa,
SPA_FEATURE_CHACHA20_POLY1305, tx);
}
/* create the root dataset */ /* create the root dataset */
obj = dsl_dataset_create_sync_dd(dp->dp_root_dir, NULL, dcp, 0, tx); obj = dsl_dataset_create_sync_dd(dp->dp_root_dir, NULL, dcp, 0, tx);

View File

@ -290,7 +290,8 @@ tests = ['zfs_receive_001_pos', 'zfs_receive_002_pos', 'zfs_receive_003_pos',
'zfs_receive_raw', 'zfs_receive_raw_incremental', 'zfs_receive_-e', 'zfs_receive_raw', 'zfs_receive_raw_incremental', 'zfs_receive_-e',
'zfs_receive_raw_-d', 'zfs_receive_from_zstd', 'zfs_receive_new_props', 'zfs_receive_raw_-d', 'zfs_receive_from_zstd', 'zfs_receive_new_props',
'zfs_receive_-wR-encrypted-mix', 'zfs_receive_corrective', 'zfs_receive_-wR-encrypted-mix', 'zfs_receive_corrective',
'zfs_receive_compressed_corrective', 'zfs_receive_large_block_corrective'] 'zfs_receive_compressed_corrective', 'zfs_receive_large_block_corrective',
'zfs_receive_chapoly_feature']
tags = ['functional', 'cli_root', 'zfs_receive'] tags = ['functional', 'cli_root', 'zfs_receive']
[tests/functional/cli_root/zfs_rename] [tests/functional/cli_root/zfs_rename]

View File

@ -178,7 +178,8 @@ tests = ['zfs_receive_001_pos', 'zfs_receive_002_pos', 'zfs_receive_003_pos',
'zfs_receive_016_pos', 'zfs_receive_from_encrypted', 'zfs_receive_016_pos', 'zfs_receive_from_encrypted',
'zfs_receive_to_encrypted', 'zfs_receive_raw', 'zfs_receive_to_encrypted', 'zfs_receive_raw',
'zfs_receive_raw_incremental', 'zfs_receive_-e', 'zfs_receive_raw_incremental', 'zfs_receive_-e',
'zfs_receive_raw_-d', 'zfs_receive_from_zstd', 'zfs_receive_new_props'] 'zfs_receive_raw_-d', 'zfs_receive_from_zstd', 'zfs_receive_new_props',
'zfs_receive_chapoly_feature']
tags = ['functional', 'cli_root', 'zfs_receive'] tags = ['functional', 'cli_root', 'zfs_receive']
[tests/functional/cli_root/zfs_rename] [tests/functional/cli_root/zfs_rename]

View File

@ -27,6 +27,11 @@ scripts_zfs_tests_functional_tmpfile_PROGRAMS = \
%D%/tests/functional/tmpfile/tmpfile_003_pos \ %D%/tests/functional/tmpfile/tmpfile_003_pos \
%D%/tests/functional/tmpfile/tmpfile_stat_mode \ %D%/tests/functional/tmpfile/tmpfile_stat_mode \
%D%/tests/functional/tmpfile/tmpfile_test %D%/tests/functional/tmpfile/tmpfile_test
scripts_zfs_tests_functional_chapolydir = $(datadir)/$(PACKAGE)/zfs-tests/tests/functional/chapoly
scripts_zfs_tests_functional_chapoly_PROGRAMS = %D%/tests/functional/chapoly/chapoly_test
%C%_tests_functional_chapoly_chapoly_test_LDADD = \
libzpool.la
endif endif

View File

@ -827,6 +827,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
functional/cli_root/zfs_receive/zfs_receive_corrective.ksh \ functional/cli_root/zfs_receive/zfs_receive_corrective.ksh \
functional/cli_root/zfs_receive/zfs_receive_compressed_corrective.ksh \ functional/cli_root/zfs_receive/zfs_receive_compressed_corrective.ksh \
functional/cli_root/zfs_receive/zfs_receive_large_block_corrective.ksh \ functional/cli_root/zfs_receive/zfs_receive_large_block_corrective.ksh \
functional/cli_root/zfs_receive/zfs_receive_chapoly_feature.ksh \
functional/cli_root/zfs_rename/cleanup.ksh \ functional/cli_root/zfs_rename/cleanup.ksh \
functional/cli_root/zfs_rename/setup.ksh \ functional/cli_root/zfs_rename/setup.ksh \
functional/cli_root/zfs_rename/zfs_rename_001_pos.ksh \ functional/cli_root/zfs_rename/zfs_rename_001_pos.ksh \

View File

@ -0,0 +1 @@
chapoly_test

View File

@ -0,0 +1,814 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or https://opensource.org/licenses/CDDL-1.0.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2024, Rob Norris <robn@despairlabs.com>
*/
/*
* This program runs the test vectors from RFC 8439 to ensure the bundled
* implementations of Chacha20 and Poly1305 are hooked up properly, and then
* tests the Chacha20-Poly1305 ICP module to ensure it properly implements the
* AEAD.
*
* This is mostly useful to verify that alternate implementations of the
* algorithms (eg accelerated versions) do the right thing, as the
* implementations out there are highly variable in function and quality and
* its often very difficult to tell if they're producing the right results.
*
* That said, these tests passing doesn't say anything about the security
* characteristics of these algorithms as used in OpenZFS, only that the
* underlying implementations are probably not entirely broken.
*/
#include <sys/crypto/icp.h>
#include <sys/crypto/api.h>
#include <monocypher.h>
static void
hexdump(const char *str, const uint8_t *src, uint_t len)
{
printf("%12s:", str);
int i = 0;
while (i < len) {
if (i % 4 == 0)
printf(" ");
printf("%02x", src[i]);
i++;
if (i % 16 == 0 && i < len) {
printf("\n");
if (i < len)
printf(" ");
}
}
printf("\n");
}
static uint8_t PLAINTEXT_SUNSCREEN[] = {
0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, /* Ladies a */
0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c, /* nd Gentl */
0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20, /* emen of */
0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73, /* the clas */
0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39, /* s of '99 */
0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63, /* : If I c */
0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x66, /* ould off */
0x65, 0x72, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6f, /* er you o */
0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20, /* nly one */
0x74, 0x69, 0x70, 0x20, 0x66, 0x6f, 0x72, 0x20, /* tip for */
0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x74, 0x75, /* the futu */
0x72, 0x65, 0x2c, 0x20, 0x73, 0x75, 0x6e, 0x73, /* re, suns */
0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f, /* creen wo */
0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69, /* uld be i */
0x74, 0x2e /* t. */
};
static uint8_t PLAINTEXT_IETF[] = {
0x41, 0x6e, 0x79, 0x20, 0x73, 0x75, 0x62, 0x6d, /* Any subm */
0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x74, /* ission t */
0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x49, 0x45, /* o the IE */
0x54, 0x46, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x6e, /* TF inten */
0x64, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, /* ded by t */
0x68, 0x65, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, /* he Contr */
0x69, 0x62, 0x75, 0x74, 0x6f, 0x72, 0x20, 0x66, /* ibutor f */
0x6f, 0x72, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, /* or publi */
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, /* cation a */
0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6f, 0x72, /* s all or */
0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66, /* part of */
0x20, 0x61, 0x6e, 0x20, 0x49, 0x45, 0x54, 0x46, /* an IETF */
0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, /* Internet */
0x74, 0x2d, 0x44, 0x72, 0x61, 0x66, 0x74, 0x20, /* -Draft */
0x6f, 0x72, 0x20, 0x52, 0x46, 0x43, 0x20, 0x61, /* or RFC a */
0x6e, 0x64, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x73, /* nd any s */
0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, /* tatement */
0x20, 0x6d, 0x61, 0x64, 0x65, 0x20, 0x77, 0x69, /* made wi */
0x74, 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, /* thin the */
0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, /* context */
0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x49, /* of an IE */
0x45, 0x54, 0x46, 0x20, 0x61, 0x63, 0x74, 0x69, /* TF acti */
0x76, 0x69, 0x74, 0x79, 0x20, 0x69, 0x73, 0x20, /* vity is */
0x63, 0x6f, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72, /* consider */
0x65, 0x64, 0x20, 0x61, 0x6e, 0x20, 0x22, 0x49, /* ed an "I */
0x45, 0x54, 0x46, 0x20, 0x43, 0x6f, 0x6e, 0x74, /* ETF Cont */
0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, /* ribution */
0x22, 0x2e, 0x20, 0x53, 0x75, 0x63, 0x68, 0x20, /* ". Such */
0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, /* statemen */
0x74, 0x73, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, /* ts inclu */
0x64, 0x65, 0x20, 0x6f, 0x72, 0x61, 0x6c, 0x20, /* de oral */
0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, /* statemen */
0x74, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x49, 0x45, /* ts in IE */
0x54, 0x46, 0x20, 0x73, 0x65, 0x73, 0x73, 0x69, /* TF sessi */
0x6f, 0x6e, 0x73, 0x2c, 0x20, 0x61, 0x73, 0x20, /* ons, as */
0x77, 0x65, 0x6c, 0x6c, 0x20, 0x61, 0x73, 0x20, /* well as */
0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x20, /* written */
0x61, 0x6e, 0x64, 0x20, 0x65, 0x6c, 0x65, 0x63, /* and elec */
0x74, 0x72, 0x6f, 0x6e, 0x69, 0x63, 0x20, 0x63, /* tronic c */
0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, /* ommunica */
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6d, 0x61, /* tions ma */
0x64, 0x65, 0x20, 0x61, 0x74, 0x20, 0x61, 0x6e, /* de at an */
0x79, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x6f, /* y time o */
0x72, 0x20, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x2c, /* r place, */
0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x61, /* which ar */
0x72, 0x65, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, /* e addre */
0x73, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f /* ssed to */
};
static uint8_t PLAINTEXT_IETF2[] = {
0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, /* Internet */
0x2d, 0x44, 0x72, 0x61, 0x66, 0x74, 0x73, 0x20, /* -Drafts */
0x61, 0x72, 0x65, 0x20, 0x64, 0x72, 0x61, 0x66, /* are draf */
0x74, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, /* t docume */
0x6e, 0x74, 0x73, 0x20, 0x76, 0x61, 0x6c, 0x69, /* nts vali */
0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, /* d for a */
0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x20, /* maximum */
0x6f, 0x66, 0x20, 0x73, 0x69, 0x78, 0x20, 0x6d, /* of six m */
0x6f, 0x6e, 0x74, 0x68, 0x73, 0x20, 0x61, 0x6e, /* onths an */
0x64, 0x20, 0x6d, 0x61, 0x79, 0x20, 0x62, 0x65, /* d may be */
0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, /* updated */
0x2c, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, /* , replac */
0x65, 0x64, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x6f, /* ed, or o */
0x62, 0x73, 0x6f, 0x6c, 0x65, 0x74, 0x65, 0x64, /* bsoleted */
0x20, 0x62, 0x79, 0x20, 0x6f, 0x74, 0x68, 0x65, /* by othe */
0x72, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, /* r docume */
0x6e, 0x74, 0x73, 0x20, 0x61, 0x74, 0x20, 0x61, /* nts at a */
0x6e, 0x79, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x2e, /* ny time. */
0x20, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x69, /* It is i */
0x6e, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x70, 0x72, /* nappropr */
0x69, 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, /* iate to */
0x75, 0x73, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65, /* use Inte */
0x72, 0x6e, 0x65, 0x74, 0x2d, 0x44, 0x72, 0x61, /* rnet-Dra */
0x66, 0x74, 0x73, 0x20, 0x61, 0x73, 0x20, 0x72, /* fts as r */
0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, /* eference */
0x20, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, /* materia */
0x6c, 0x20, 0x6f, 0x72, 0x20, 0x74, 0x6f, 0x20, /* l or to */
0x63, 0x69, 0x74, 0x65, 0x20, 0x74, 0x68, 0x65, /* cite the */
0x6d, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, /* m other */
0x74, 0x68, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x20, /* than as */
0x2f, 0xe2, 0x80, 0x9c, 0x77, 0x6f, 0x72, 0x6b, /* /...work */
0x20, 0x69, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x67, /* in prog */
0x72, 0x65, 0x73, 0x73, 0x2e, 0x2f, 0xe2, 0x80, /* ress./.. */
0x9d /* . */
};
static uint8_t PLAINTEXT_JABBERWOCKY[] = {
0x27, 0x54, 0x77, 0x61, 0x73, 0x20, 0x62, 0x72, /* 'Twas br */
0x69, 0x6c, 0x6c, 0x69, 0x67, 0x2c, 0x20, 0x61, /* illig, a */
0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, /* nd the s */
0x6c, 0x69, 0x74, 0x68, 0x79, 0x20, 0x74, 0x6f, /* lithy to */
0x76, 0x65, 0x73, 0x0a, 0x44, 0x69, 0x64, 0x20, /* ves.Did */
0x67, 0x79, 0x72, 0x65, 0x20, 0x61, 0x6e, 0x64, /* gyre and */
0x20, 0x67, 0x69, 0x6d, 0x62, 0x6c, 0x65, 0x20, /* gimble */
0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, /* in the w */
0x61, 0x62, 0x65, 0x3a, 0x0a, 0x41, 0x6c, 0x6c, /* abe:.All */
0x20, 0x6d, 0x69, 0x6d, 0x73, 0x79, 0x20, 0x77, /* mimsy w */
0x65, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, /* ere the */
0x62, 0x6f, 0x72, 0x6f, 0x67, 0x6f, 0x76, 0x65, /* borogove */
0x73, 0x2c, 0x0a, 0x41, 0x6e, 0x64, 0x20, 0x74, /* s,.And t */
0x68, 0x65, 0x20, 0x6d, 0x6f, 0x6d, 0x65, 0x20, /* he mome */
0x72, 0x61, 0x74, 0x68, 0x73, 0x20, 0x6f, 0x75, /* raths ou */
0x74, 0x67, 0x72, 0x61, 0x62, 0x65, 0x2e /* tgrabe. */
};
static uint8_t PLAINTEXT_CFRG[] = {
0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x67, 0x72, /* Cryptogr */
0x61, 0x70, 0x68, 0x69, 0x63, 0x20, 0x46, 0x6f, /* aphic Fo */
0x72, 0x75, 0x6d, 0x20, 0x52, 0x65, 0x73, 0x65, /* rum Rese */
0x61, 0x72, 0x63, 0x68, 0x20, 0x47, 0x72, 0x6f, /* arch Gro */
0x75, 0x70 /* up */
};
typedef struct {
const char *name;
const uint8_t key[32];
const uint8_t nonce[12];
uint32_t counter;
const uint8_t *plaintext;
const uint8_t *ciphertext;
size_t textlen;
} chacha_test_t;
static const chacha_test_t chacha_tests[] = {
{
.name = "RFC 8439 2.4.2",
.key = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
},
.nonce = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x4a,
0x00, 0x00, 0x00, 0x00
},
.counter = 1,
.plaintext = PLAINTEXT_SUNSCREEN,
.ciphertext = (uint8_t *)&(uint8_t[]) {
0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68, 0xf9, 0x80,
0x41, 0xba, 0x07, 0x28, 0xdd, 0x0d, 0x69, 0x81,
0xe9, 0x7e, 0x7a, 0xec, 0x1d, 0x43, 0x60, 0xc2,
0x0a, 0x27, 0xaf, 0xcc, 0xfd, 0x9f, 0xae, 0x0b,
0xf9, 0x1b, 0x65, 0xc5, 0x52, 0x47, 0x33, 0xab,
0x8f, 0x59, 0x3d, 0xab, 0xcd, 0x62, 0xb3, 0x57,
0x16, 0x39, 0xd6, 0x24, 0xe6, 0x51, 0x52, 0xab,
0x8f, 0x53, 0x0c, 0x35, 0x9f, 0x08, 0x61, 0xd8,
0x07, 0xca, 0x0d, 0xbf, 0x50, 0x0d, 0x6a, 0x61,
0x56, 0xa3, 0x8e, 0x08, 0x8a, 0x22, 0xb6, 0x5e,
0x52, 0xbc, 0x51, 0x4d, 0x16, 0xcc, 0xf8, 0x06,
0x81, 0x8c, 0xe9, 0x1a, 0xb7, 0x79, 0x37, 0x36,
0x5a, 0xf9, 0x0b, 0xbf, 0x74, 0xa3, 0x5b, 0xe6,
0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42,
0x87, 0x4d
},
.textlen = sizeof (PLAINTEXT_SUNSCREEN),
}, {
.name = "RFC 8439 A.2 #1",
.key = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
},
.nonce = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
},
.counter = 0,
.plaintext = (uint8_t *)&(uint8_t[64]) { 0 },
.ciphertext = (uint8_t *)&(uint8_t[]) {
0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90,
0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, 0x28,
0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a,
0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, 0x0d, 0xc7,
0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d,
0x77, 0x24, 0xe0, 0x3f, 0xb8, 0xd8, 0x4a, 0x37,
0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c,
0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86
},
.textlen = 64,
}, {
.name = "RFC 8439 A.2 #2",
.key = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
},
.nonce = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02
},
.counter = 1,
.plaintext = PLAINTEXT_IETF,
.ciphertext = (uint8_t *)&(uint8_t[]) {
0xa3, 0xfb, 0xf0, 0x7d, 0xf3, 0xfa, 0x2f, 0xde,
0x4f, 0x37, 0x6c, 0xa2, 0x3e, 0x82, 0x73, 0x70,
0x41, 0x60, 0x5d, 0x9f, 0x4f, 0x4f, 0x57, 0xbd,
0x8c, 0xff, 0x2c, 0x1d, 0x4b, 0x79, 0x55, 0xec,
0x2a, 0x97, 0x94, 0x8b, 0xd3, 0x72, 0x29, 0x15,
0xc8, 0xf3, 0xd3, 0x37, 0xf7, 0xd3, 0x70, 0x05,
0x0e, 0x9e, 0x96, 0xd6, 0x47, 0xb7, 0xc3, 0x9f,
0x56, 0xe0, 0x31, 0xca, 0x5e, 0xb6, 0x25, 0x0d,
0x40, 0x42, 0xe0, 0x27, 0x85, 0xec, 0xec, 0xfa,
0x4b, 0x4b, 0xb5, 0xe8, 0xea, 0xd0, 0x44, 0x0e,
0x20, 0xb6, 0xe8, 0xdb, 0x09, 0xd8, 0x81, 0xa7,
0xc6, 0x13, 0x2f, 0x42, 0x0e, 0x52, 0x79, 0x50,
0x42, 0xbd, 0xfa, 0x77, 0x73, 0xd8, 0xa9, 0x05,
0x14, 0x47, 0xb3, 0x29, 0x1c, 0xe1, 0x41, 0x1c,
0x68, 0x04, 0x65, 0x55, 0x2a, 0xa6, 0xc4, 0x05,
0xb7, 0x76, 0x4d, 0x5e, 0x87, 0xbe, 0xa8, 0x5a,
0xd0, 0x0f, 0x84, 0x49, 0xed, 0x8f, 0x72, 0xd0,
0xd6, 0x62, 0xab, 0x05, 0x26, 0x91, 0xca, 0x66,
0x42, 0x4b, 0xc8, 0x6d, 0x2d, 0xf8, 0x0e, 0xa4,
0x1f, 0x43, 0xab, 0xf9, 0x37, 0xd3, 0x25, 0x9d,
0xc4, 0xb2, 0xd0, 0xdf, 0xb4, 0x8a, 0x6c, 0x91,
0x39, 0xdd, 0xd7, 0xf7, 0x69, 0x66, 0xe9, 0x28,
0xe6, 0x35, 0x55, 0x3b, 0xa7, 0x6c, 0x5c, 0x87,
0x9d, 0x7b, 0x35, 0xd4, 0x9e, 0xb2, 0xe6, 0x2b,
0x08, 0x71, 0xcd, 0xac, 0x63, 0x89, 0x39, 0xe2,
0x5e, 0x8a, 0x1e, 0x0e, 0xf9, 0xd5, 0x28, 0x0f,
0xa8, 0xca, 0x32, 0x8b, 0x35, 0x1c, 0x3c, 0x76,
0x59, 0x89, 0xcb, 0xcf, 0x3d, 0xaa, 0x8b, 0x6c,
0xcc, 0x3a, 0xaf, 0x9f, 0x39, 0x79, 0xc9, 0x2b,
0x37, 0x20, 0xfc, 0x88, 0xdc, 0x95, 0xed, 0x84,
0xa1, 0xbe, 0x05, 0x9c, 0x64, 0x99, 0xb9, 0xfd,
0xa2, 0x36, 0xe7, 0xe8, 0x18, 0xb0, 0x4b, 0x0b,
0xc3, 0x9c, 0x1e, 0x87, 0x6b, 0x19, 0x3b, 0xfe,
0x55, 0x69, 0x75, 0x3f, 0x88, 0x12, 0x8c, 0xc0,
0x8a, 0xaa, 0x9b, 0x63, 0xd1, 0xa1, 0x6f, 0x80,
0xef, 0x25, 0x54, 0xd7, 0x18, 0x9c, 0x41, 0x1f,
0x58, 0x69, 0xca, 0x52, 0xc5, 0xb8, 0x3f, 0xa3,
0x6f, 0xf2, 0x16, 0xb9, 0xc1, 0xd3, 0x00, 0x62,
0xbe, 0xbc, 0xfd, 0x2d, 0xc5, 0xbc, 0xe0, 0x91,
0x19, 0x34, 0xfd, 0xa7, 0x9a, 0x86, 0xf6, 0xe6,
0x98, 0xce, 0xd7, 0x59, 0xc3, 0xff, 0x9b, 0x64,
0x77, 0x33, 0x8f, 0x3d, 0xa4, 0xf9, 0xcd, 0x85,
0x14, 0xea, 0x99, 0x82, 0xcc, 0xaf, 0xb3, 0x41,
0xb2, 0x38, 0x4d, 0xd9, 0x02, 0xf3, 0xd1, 0xab,
0x7a, 0xc6, 0x1d, 0xd2, 0x9c, 0x6f, 0x21, 0xba,
0x5b, 0x86, 0x2f, 0x37, 0x30, 0xe3, 0x7c, 0xfd,
0xc4, 0xfd, 0x80, 0x6c, 0x22, 0xf2, 0x21
},
.textlen = sizeof (PLAINTEXT_IETF),
}, {
.name = "RFC 8439 A.2 #3",
.key = {
0x1c, 0x92, 0x40, 0xa5, 0xeb, 0x55, 0xd3, 0x8a,
0xf3, 0x33, 0x88, 0x86, 0x04, 0xf6, 0xb5, 0xf0,
0x47, 0x39, 0x17, 0xc1, 0x40, 0x2b, 0x80, 0x09,
0x9d, 0xca, 0x5c, 0xbc, 0x20, 0x70, 0x75, 0xc0
},
.nonce = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02
},
.counter = 42,
.plaintext = PLAINTEXT_JABBERWOCKY,
.ciphertext = (uint8_t *)&(uint8_t[]) {
0x62, 0xe6, 0x34, 0x7f, 0x95, 0xed, 0x87, 0xa4,
0x5f, 0xfa, 0xe7, 0x42, 0x6f, 0x27, 0xa1, 0xdf,
0x5f, 0xb6, 0x91, 0x10, 0x04, 0x4c, 0x0d, 0x73,
0x11, 0x8e, 0xff, 0xa9, 0x5b, 0x01, 0xe5, 0xcf,
0x16, 0x6d, 0x3d, 0xf2, 0xd7, 0x21, 0xca, 0xf9,
0xb2, 0x1e, 0x5f, 0xb1, 0x4c, 0x61, 0x68, 0x71,
0xfd, 0x84, 0xc5, 0x4f, 0x9d, 0x65, 0xb2, 0x83,
0x19, 0x6c, 0x7f, 0xe4, 0xf6, 0x05, 0x53, 0xeb,
0xf3, 0x9c, 0x64, 0x02, 0xc4, 0x22, 0x34, 0xe3,
0x2a, 0x35, 0x6b, 0x3e, 0x76, 0x43, 0x12, 0xa6,
0x1a, 0x55, 0x32, 0x05, 0x57, 0x16, 0xea, 0xd6,
0x96, 0x25, 0x68, 0xf8, 0x7d, 0x3f, 0x3f, 0x77,
0x04, 0xc6, 0xa8, 0xd1, 0xbc, 0xd1, 0xbf, 0x4d,
0x50, 0xd6, 0x15, 0x4b, 0x6d, 0xa7, 0x31, 0xb1,
0x87, 0xb5, 0x8d, 0xfd, 0x72, 0x8a, 0xfa, 0x36,
0x75, 0x7a, 0x79, 0x7a, 0xc1, 0x88, 0xd1
},
.textlen = sizeof (PLAINTEXT_JABBERWOCKY),
}, {
.name = NULL,
} };
static int
test_chacha(void)
{
uint8_t outbuf[1024];
int failed = 0;
for (int testno = 0; chacha_tests[testno].name; testno++) {
const chacha_test_t *test = &chacha_tests[testno];
printf("chacha test: %s: ", test->name);
crypto_chacha20_ietf(
outbuf, test->plaintext, test->textlen,
test->key, test->nonce, test->counter);
if (memcmp(outbuf, test->ciphertext, test->textlen) != 0) {
printf("FAIL\n");
printf(" ciphertexts don't match:\n");
hexdump("got", outbuf, test->textlen);
hexdump("expected", test->ciphertext, test->textlen);
failed |= 1;
}
else {
printf("SUCCESS\n");
}
}
return (failed);
}
typedef struct {
const char *name;
const uint8_t key[32];
const uint8_t *text;
size_t textlen;
const uint8_t tag[16];
} poly_test_t;
static const poly_test_t poly_tests[] = {
{
.name = "RFC 8439 2.5.2",
.key = {
0x85, 0xd6, 0xbe, 0x78, 0x57, 0x55, 0x6d, 0x33,
0x7f, 0x44, 0x52, 0xfe, 0x42, 0xd5, 0x06, 0xa8,
0x01, 0x03, 0x80, 0x8a, 0xfb, 0x0d, 0xb2, 0xfd,
0x4a, 0xbf, 0xf6, 0xaf, 0x41, 0x49, 0xf5, 0x1b
},
.text = PLAINTEXT_CFRG,
.textlen = sizeof (PLAINTEXT_CFRG),
.tag = {
0xa8, 0x06, 0x1d, 0xc1, 0x30, 0x51, 0x36, 0xc6,
0xc2, 0x2b, 0x8b, 0xaf, 0x0c, 0x01, 0x27, 0xa9
},
}, {
.name = "RFC 8439 A.3 #1",
.key = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
.text = (uint8_t *)&(uint8_t[]) {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
},
.textlen = 64,
.tag = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
},
}, {
.name = "RFC 8439 A.3 #2",
.key = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x36, 0xe5, 0xf6, 0xb5, 0xc5, 0xe0, 0x60, 0x70,
0xf0, 0xef, 0xca, 0x96, 0x22, 0x7a, 0x86, 0x3e
},
.text = PLAINTEXT_IETF,
.textlen = sizeof (PLAINTEXT_IETF),
.tag = {
0x36, 0xe5, 0xf6, 0xb5, 0xc5, 0xe0, 0x60, 0x70,
0xf0, 0xef, 0xca, 0x96, 0x22, 0x7a, 0x86, 0x3e
},
}, {
.name = "RFC 8439 A.3 #3",
.key = {
0x36, 0xe5, 0xf6, 0xb5, 0xc5, 0xe0, 0x60, 0x70,
0xf0, 0xef, 0xca, 0x96, 0x22, 0x7a, 0x86, 0x3e,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
},
.text = PLAINTEXT_IETF,
.textlen = sizeof (PLAINTEXT_IETF),
.tag = {
0xf3, 0x47, 0x7e, 0x7c, 0xd9, 0x54, 0x17, 0xaf,
0x89, 0xa6, 0xb8, 0x79, 0x4c, 0x31, 0x0c, 0xf0
},
}, {
.name = "RFC 8439 A.3 #4",
.key = {
0x1c, 0x92, 0x40, 0xa5, 0xeb, 0x55, 0xd3, 0x8a,
0xf3, 0x33, 0x88, 0x86, 0x04, 0xf6, 0xb5, 0xf0,
0x47, 0x39, 0x17, 0xc1, 0x40, 0x2b, 0x80, 0x09,
0x9d, 0xca, 0x5c, 0xbc, 0x20, 0x70, 0x75, 0xc0
},
.text = PLAINTEXT_JABBERWOCKY,
.textlen = sizeof (PLAINTEXT_JABBERWOCKY),
.tag = {
0x45, 0x41, 0x66, 0x9a, 0x7e, 0xaa, 0xee, 0x61,
0xe7, 0x08, 0xdc, 0x7c, 0xbc, 0xc5, 0xeb, 0x62
},
}, {
.name = NULL,
} };
static int
test_poly(void)
{
uint8_t macbuf[16];
int failed = 0;
for (int testno = 0; poly_tests[testno].name; testno++) {
const poly_test_t *test = &poly_tests[testno];
printf("poly test: %s: ", test->name);
crypto_poly1305_ctx poly;
crypto_poly1305_init(&poly, test->key);
crypto_poly1305_update(&poly, test->text, test->textlen);
crypto_poly1305_final(&poly, macbuf);
if (memcmp(test->tag, macbuf, 16) != 0) {
printf("FAIL\n");
printf(" tags don't match:\n");
hexdump("got", macbuf, 16);
hexdump("expected", test->tag, 16);
failed |= 1;
}
else {
printf("SUCCESS\n");
}
}
return (failed);
}
typedef struct {
const char *name;
const uint8_t key[32];
const uint8_t nonce[12];
const uint8_t *aad;
size_t aadlen;
const uint8_t *plaintext;
const uint8_t *ciphertext;
size_t textlen;
const uint8_t *tag;
size_t taglen;
} module_test_t;
static const module_test_t module_tests[] = {
{
.name = "RFC 8439 2.8.2",
.key = {
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f
},
.nonce = {
0x07, 0x00, 0x00, 0x00,
0x40, 0x41, 0x42, 0x43,
0x44, 0x45, 0x46, 0x47
},
.aad = (uint8_t *)&(uint8_t[]) {
0x50, 0x51, 0x52, 0x53,
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7
},
.aadlen = 12,
.plaintext = PLAINTEXT_SUNSCREEN,
.ciphertext = (uint8_t *)&(uint8_t[]) {
0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb,
0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2,
0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe,
0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6,
0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12,
0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b,
0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29,
0x05, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36,
0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c,
0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58,
0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94,
0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc,
0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d,
0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b,
0x61, 0x16
},
.textlen = sizeof (PLAINTEXT_SUNSCREEN),
.tag = (uint8_t *)&(uint8_t[]) {
0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09, 0xe2, 0x6a,
0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, 0x91
},
.taglen = 16,
}, {
.name = "RFC 8439 A.5",
.key = {
0x1c, 0x92, 0x40, 0xa5, 0xeb, 0x55, 0xd3, 0x8a,
0xf3, 0x33, 0x88, 0x86, 0x04, 0xf6, 0xb5, 0xf0,
0x47, 0x39, 0x17, 0xc1, 0x40, 0x2b, 0x80, 0x09,
0x9d, 0xca, 0x5c, 0xbc, 0x20, 0x70, 0x75, 0xc0
},
.nonce = {
0x00, 0x00, 0x00, 0x00,
0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07, 0x08
},
.aad = (uint8_t *)&(uint8_t[]) {
0xf3, 0x33, 0x88, 0x86,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x91
},
.aadlen = 12,
.plaintext = PLAINTEXT_IETF2,
.ciphertext = (uint8_t *)&(uint8_t[]) {
0x64, 0xa0, 0x86, 0x15, 0x75, 0x86, 0x1a, 0xf4,
0x60, 0xf0, 0x62, 0xc7, 0x9b, 0xe6, 0x43, 0xbd,
0x5e, 0x80, 0x5c, 0xfd, 0x34, 0x5c, 0xf3, 0x89,
0xf1, 0x08, 0x67, 0x0a, 0xc7, 0x6c, 0x8c, 0xb2,
0x4c, 0x6c, 0xfc, 0x18, 0x75, 0x5d, 0x43, 0xee,
0xa0, 0x9e, 0xe9, 0x4e, 0x38, 0x2d, 0x26, 0xb0,
0xbd, 0xb7, 0xb7, 0x3c, 0x32, 0x1b, 0x01, 0x00,
0xd4, 0xf0, 0x3b, 0x7f, 0x35, 0x58, 0x94, 0xcf,
0x33, 0x2f, 0x83, 0x0e, 0x71, 0x0b, 0x97, 0xce,
0x98, 0xc8, 0xa8, 0x4a, 0xbd, 0x0b, 0x94, 0x81,
0x14, 0xad, 0x17, 0x6e, 0x00, 0x8d, 0x33, 0xbd,
0x60, 0xf9, 0x82, 0xb1, 0xff, 0x37, 0xc8, 0x55,
0x97, 0x97, 0xa0, 0x6e, 0xf4, 0xf0, 0xef, 0x61,
0xc1, 0x86, 0x32, 0x4e, 0x2b, 0x35, 0x06, 0x38,
0x36, 0x06, 0x90, 0x7b, 0x6a, 0x7c, 0x02, 0xb0,
0xf9, 0xf6, 0x15, 0x7b, 0x53, 0xc8, 0x67, 0xe4,
0xb9, 0x16, 0x6c, 0x76, 0x7b, 0x80, 0x4d, 0x46,
0xa5, 0x9b, 0x52, 0x16, 0xcd, 0xe7, 0xa4, 0xe9,
0x90, 0x40, 0xc5, 0xa4, 0x04, 0x33, 0x22, 0x5e,
0xe2, 0x82, 0xa1, 0xb0, 0xa0, 0x6c, 0x52, 0x3e,
0xaf, 0x45, 0x34, 0xd7, 0xf8, 0x3f, 0xa1, 0x15,
0x5b, 0x00, 0x47, 0x71, 0x8c, 0xbc, 0x54, 0x6a,
0x0d, 0x07, 0x2b, 0x04, 0xb3, 0x56, 0x4e, 0xea,
0x1b, 0x42, 0x22, 0x73, 0xf5, 0x48, 0x27, 0x1a,
0x0b, 0xb2, 0x31, 0x60, 0x53, 0xfa, 0x76, 0x99,
0x19, 0x55, 0xeb, 0xd6, 0x31, 0x59, 0x43, 0x4e,
0xce, 0xbb, 0x4e, 0x46, 0x6d, 0xae, 0x5a, 0x10,
0x73, 0xa6, 0x72, 0x76, 0x27, 0x09, 0x7a, 0x10,
0x49, 0xe6, 0x17, 0xd9, 0x1d, 0x36, 0x10, 0x94,
0xfa, 0x68, 0xf0, 0xff, 0x77, 0x98, 0x71, 0x30,
0x30, 0x5b, 0xea, 0xba, 0x2e, 0xda, 0x04, 0xdf,
0x99, 0x7b, 0x71, 0x4d, 0x6c, 0x6f, 0x2c, 0x29,
0xa6, 0xad, 0x5c, 0xb4, 0x02, 0x2b, 0x02, 0x70,
0x9b
},
.textlen = sizeof (PLAINTEXT_IETF2),
.tag = (uint8_t *)&(uint8_t[]) {
0xee, 0xad, 0x9d, 0x67, 0x89, 0x0c, 0xbb, 0x22,
0x39, 0x23, 0x36, 0xfe, 0xa1, 0x85, 0x1f, 0x38
},
.taglen = 16,
}, {
.name = NULL,
} };
static int
test_module_encrypt(const module_test_t *test)
{
crypto_mechanism_t mech;
uint8_t outbuf[1024];
mech.cm_type = crypto_mech2id(SUN_CKM_CHACHA20_POLY1305);
printf("module test: %s [encrypt]: ", test->name);
CK_AES_GCM_PARAMS gcmp = {
.pIv = (uchar_t *)test->nonce,
.ulIvLen = sizeof (test->nonce),
.ulIvBits = CRYPTO_BYTES2BITS(sizeof (test->nonce)),
.pAAD = (uint8_t *)test->aad,
.ulAADLen = test->aadlen,
.ulTagBits = CRYPTO_BYTES2BITS(test->taglen),
};
mech.cm_param = (char *)&gcmp;
mech.cm_param_len = sizeof (CK_AES_GCM_PARAMS);
crypto_key_t key = {
.ck_length = sizeof (test->key) << 3,
.ck_data = (uint8_t *)test->key,
};
crypto_data_t in = {
.cd_format = CRYPTO_DATA_RAW,
.cd_offset = 0,
.cd_length = test->textlen,
.cd_raw = {
.iov_base = (char *)test->plaintext,
.iov_len = test->textlen,
},
};
crypto_data_t out = {
.cd_format = CRYPTO_DATA_RAW,
.cd_offset = 0,
.cd_length = test->textlen + test->taglen,
.cd_raw = {
.iov_base = (char *)outbuf,
.iov_len = sizeof (outbuf),
},
};
int rv = crypto_encrypt(&mech, &in, &key, NULL, &out);
if (rv != CRYPTO_SUCCESS) {
printf("FAIL\n");
printf(" encrypt rv = 0x%02x\n", rv);
return (1);
}
if (memcmp(outbuf, test->ciphertext, test->textlen) != 0) {
printf("FAIL\n");
printf(" ciphertexts don't match:\n");
hexdump("got", outbuf, test->textlen);
hexdump("expected", test->ciphertext, test->textlen);
return (1);
}
if (memcmp(outbuf + test->textlen, test->tag, test->taglen) != 0) {
printf("FAIL\n");
printf(" tags don't match:\n");
hexdump("got", outbuf + test->textlen, test->taglen);
hexdump("expected", test->tag, test->taglen);
return (1);
}
printf("SUCCESS\n");
return (0);
}
static int
test_module_decrypt(const module_test_t *test)
{
crypto_mechanism_t mech;
uint8_t inbuf[1024], outbuf[1024];
mech.cm_type = crypto_mech2id(SUN_CKM_CHACHA20_POLY1305);
printf("module test: %s [decrypt]: ", test->name);
CK_AES_GCM_PARAMS gcmp = {
.pIv = (uchar_t *)test->nonce,
.ulIvLen = sizeof (test->nonce),
.ulIvBits = CRYPTO_BYTES2BITS(sizeof (test->nonce)),
.pAAD = (uint8_t *)test->aad,
.ulAADLen = test->aadlen,
.ulTagBits = CRYPTO_BYTES2BITS(test->taglen),
};
mech.cm_param = (char *)&gcmp;
mech.cm_param_len = sizeof (CK_AES_GCM_PARAMS);
crypto_key_t key = {
.ck_length = sizeof (test->key) << 3,
.ck_data = (uint8_t *)test->key,
};
memcpy(inbuf, test->ciphertext, test->textlen);
memcpy(inbuf + test->textlen, test->tag, test->taglen);
crypto_data_t in = {
.cd_format = CRYPTO_DATA_RAW,
.cd_offset = 0,
.cd_length = test->textlen + test->taglen,
.cd_raw = {
.iov_base = (char *)inbuf,
.iov_len = test->textlen + test->taglen,
},
};
crypto_data_t out = {
.cd_format = CRYPTO_DATA_RAW,
.cd_offset = 0,
.cd_length = test->textlen,
.cd_raw = {
.iov_base = (char *)outbuf,
.iov_len = sizeof (outbuf),
},
};
int rv = crypto_decrypt(&mech, &in, &key, NULL, &out);
if (rv != CRYPTO_SUCCESS) {
printf("FAIL\n");
printf(" decrypt rv = 0x%02x\n", rv);
return (1);
}
if (memcmp(outbuf, test->plaintext, test->textlen) != 0) {
printf("FAIL\n");
printf(" plaintexts don't match:\n");
hexdump("got", outbuf, test->textlen);
hexdump("expected", test->plaintext, test->textlen);
return (1);
}
printf("SUCCESS\n");
return (0);
}
static int
test_module(void)
{
int failed = 0;
icp_init();
for (int testno = 0; module_tests[testno].name; testno++) {
const module_test_t *test = &module_tests[testno];
failed |= test_module_encrypt(test);
failed |= test_module_decrypt(test);
}
icp_fini();
return (failed);
}
int
main(void)
{
int failed = 0;
failed |= test_chacha();
failed |= test_poly();
failed |= test_module();
return (failed);
}

View File

@ -50,7 +50,8 @@ set -A ENCRYPTION_ALGS \
"encryption=aes-256-ccm" \ "encryption=aes-256-ccm" \
"encryption=aes-128-gcm" \ "encryption=aes-128-gcm" \
"encryption=aes-192-gcm" \ "encryption=aes-192-gcm" \
"encryption=aes-256-gcm" "encryption=aes-256-gcm" \
"encryption=chacha20-poly1305"
set -A ENCRYPTION_PROPS \ set -A ENCRYPTION_PROPS \
"encryption=aes-256-gcm" \ "encryption=aes-256-gcm" \
@ -59,7 +60,8 @@ set -A ENCRYPTION_PROPS \
"encryption=aes-256-ccm" \ "encryption=aes-256-ccm" \
"encryption=aes-128-gcm" \ "encryption=aes-128-gcm" \
"encryption=aes-192-gcm" \ "encryption=aes-192-gcm" \
"encryption=aes-256-gcm" "encryption=aes-256-gcm" \
"encryption=chacha20-poly1305"
set -A KEYFORMATS "keyformat=raw" \ set -A KEYFORMATS "keyformat=raw" \
"keyformat=hex" \ "keyformat=hex" \

View File

@ -0,0 +1,105 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or https://opensource.org/licenses/CDDL-1.0.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright (c) 2024, Rob Norris <robn@despairlabs.com>
#
. $STF_SUITE/include/libtest.shlib
#
# DESCRIPTION:
# A raw send with a dataset with Chacha20-Poly1305 encryption should only be
# received when the feature is enabled, otherwise rejected. An AES-GCM dataset
# however should always be received.
#
verify_runnable "both"
log_assert "Raw sends with Chacha20-Poly1305 can only be" \
"recieved if feature available"
function cleanup
{
poolexists srcpool && destroy_pool srcpool
poolexists dstpool && destroy_pool dstpool
log_must rm -f $TESTDIR/srcdev $TESTDIR/dstdev
}
log_onexit cleanup
# create a pool with the the chapoly feature enabled
truncate -s $MINVDEVSIZE $TESTDIR/srcdev
log_must zpool create -f -o feature@chacha20_poly1305=enabled \
srcpool $TESTDIR/srcdev
# created encrypted filesystems
echo 'password' | create_dataset srcpool/chapoly \
-o encryption=chacha20-poly1305 -o keyformat=passphrase
echo 'password' | create_dataset srcpool/aesgcm \
-o encryption=aes-256-gcm -o keyformat=passphrase
# snapshot everything
log_must zfs snapshot -r srcpool@snap
# create a pool with the chapoly feature enabled
truncate -s $MINVDEVSIZE $TESTDIR/dstdev
log_must zpool create -f -o feature@chacha20_poly1305=enabled \
dstpool $TESTDIR/dstdev
# send and receive both filesystems
log_must eval \
"zfs send -Rw srcpool/chapoly@snap | zfs receive -u dstpool/chapoly"
log_must eval \
"zfs send -Rw srcpool/aesgcm@snap | zfs receive -u dstpool/aesgcm"
# destroy received datasets
log_must zfs destroy -r dstpool/chapoly
log_must zfs destroy -r dstpool/aesgcm
# send and receive entire recursive stream
log_must eval "zfs send -Rw srcpool@snap | zfs receive -u dstpool/all"
# remake the dest pool with the chapoly feature disabled
destroy_pool dstpool
log_must zpool create -f -o feature@chacha20_poly1305=disabled \
dstpool $TESTDIR/dstdev
# send and receive both filesystems. chapoly shoud fail, but aesgcm should
# succeed
log_mustnot eval \
"zfs send -Rw srcpool/chapoly@snap | zfs receive -u dstpool/chapoly"
log_must eval \
"zfs send -Rw srcpool/aesgcm@snap | zfs receive -u dstpool/aesgcm"
# destroy received dataset
log_must zfs destroy -r dstpool/aesgcm
# send and receive entire recursive stream, which should fail
log_mustnot eval "zfs send -Rw srcpool@snap | zfs receive -u dstpool/all"
log_pass "Chacha20-Poly1305 datasets can only be recieved" \
"if the feature is enabled"

View File

@ -46,7 +46,8 @@ set -A ENCRYPTION_ALGS "encryption=on" \
"encryption=aes-256-ccm" \ "encryption=aes-256-ccm" \
"encryption=aes-128-gcm" \ "encryption=aes-128-gcm" \
"encryption=aes-192-gcm" \ "encryption=aes-192-gcm" \
"encryption=aes-256-gcm" "encryption=aes-256-gcm" \
"encryption=chacha20-poly1305"
set -A ENCRYPTION_PROPS "encryption=aes-256-gcm" \ set -A ENCRYPTION_PROPS "encryption=aes-256-gcm" \
"encryption=aes-128-ccm" \ "encryption=aes-128-ccm" \
@ -54,7 +55,8 @@ set -A ENCRYPTION_PROPS "encryption=aes-256-gcm" \
"encryption=aes-256-ccm" \ "encryption=aes-256-ccm" \
"encryption=aes-128-gcm" \ "encryption=aes-128-gcm" \
"encryption=aes-192-gcm" \ "encryption=aes-192-gcm" \
"encryption=aes-256-gcm" "encryption=aes-256-gcm" \
"encryption=chacha20-poly1305"
set -A KEYFORMATS "keyformat=raw" \ set -A KEYFORMATS "keyformat=raw" \
"keyformat=hex" \ "keyformat=hex" \

View File

@ -89,6 +89,7 @@ typeset -a properties=(
"feature@device_rebuild" "feature@device_rebuild"
"feature@draid" "feature@draid"
"feature@redaction_list_spill" "feature@redaction_list_spill"
"feature@chacha20_poly1305"
) )
if is_linux || is_freebsd; then if is_linux || is_freebsd; then