Merge git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6
Pull crypto update from Herbert Xu: "Here is the crypto update for 4.2: API: - Convert RNG interface to new style. - New AEAD interface with one SG list for AD and plain/cipher text. All external AEAD users have been converted. - New asymmetric key interface (akcipher). Algorithms: - Chacha20, Poly1305 and RFC7539 support. - New RSA implementation. - Jitter RNG. - DRBG is now seeded with both /dev/random and Jitter RNG. If kernel pool isn't ready then DRBG will be reseeded when it is. - DRBG is now the default crypto API RNG, replacing krng. - 842 compression (previously part of powerpc nx driver). Drivers: - Accelerated SHA-512 for arm64. - New Marvell CESA driver that supports DMA and more algorithms. - Updated powerpc nx 842 support. - Added support for SEC1 hardware to talitos" * git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6: (292 commits) crypto: marvell/cesa - remove COMPILE_TEST dependency crypto: algif_aead - Temporarily disable all AEAD algorithms crypto: af_alg - Forbid the use internal algorithms crypto: echainiv - Only hold RNG during initialisation crypto: seqiv - Add compatibility support without RNG crypto: eseqiv - Offer normal cipher functionality without RNG crypto: chainiv - Offer normal cipher functionality without RNG crypto: user - Add CRYPTO_MSG_DELRNG crypto: user - Move cryptouser.h to uapi crypto: rng - Do not free default RNG when it becomes unused crypto: skcipher - Allow givencrypt to be NULL crypto: sahara - propagate the error on clk_disable_unprepare() failure crypto: rsa - fix invalid select for AKCIPHER crypto: picoxcell - Update to the current clk API crypto: nx - Check for bogus firmware properties crypto: marvell/cesa - add DT bindings documentation crypto: marvell/cesa - add support for Kirkwood and Dove SoCs crypto: marvell/cesa - add support for Orion SoCs crypto: marvell/cesa - add allhwsupport module parameter crypto: marvell/cesa - add support for all armada SoCs ...
This commit is contained in:
commit
44d21c3f3a
@ -119,7 +119,7 @@
|
||||
|
||||
<para>
|
||||
Note: The terms "transformation" and cipher algorithm are used
|
||||
interchangably.
|
||||
interchangeably.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
@ -536,8 +536,8 @@
|
||||
|
||||
<para>
|
||||
For other use cases of AEAD ciphers, the ASCII art applies as
|
||||
well, but the caller may not use the GIVCIPHER interface. In
|
||||
this case, the caller must generate the IV.
|
||||
well, but the caller may not use the AEAD cipher with a separate
|
||||
IV generator. In this case, the caller must generate the IV.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -584,8 +584,8 @@ kernel crypto API | IPSEC Layer
|
||||
|
|
||||
+-----------+ |
|
||||
| | (1)
|
||||
| givcipher | <----------------------------------- esp_output
|
||||
| (seqiv) | ---+
|
||||
| aead | <----------------------------------- esp_output
|
||||
| (seqniv) | ---+
|
||||
+-----------+ |
|
||||
| (2)
|
||||
+-----------+ |
|
||||
@ -620,8 +620,8 @@ kernel crypto API | IPSEC Layer
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
esp_output() invokes crypto_aead_givencrypt() to trigger an encryption
|
||||
operation of the GIVCIPHER implementation.
|
||||
esp_output() invokes crypto_aead_encrypt() to trigger an encryption
|
||||
operation of the AEAD cipher with IV generator.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -1563,7 +1563,7 @@ struct sockaddr_alg sa = {
|
||||
|
||||
<sect1><title>Zero-Copy Interface</title>
|
||||
<para>
|
||||
In addition to the send/write/read/recv system call familty, the AF_ALG
|
||||
In addition to the send/write/read/recv system call family, the AF_ALG
|
||||
interface can be accessed with the zero-copy interface of splice/vmsplice.
|
||||
As the name indicates, the kernel tries to avoid a copy operation into
|
||||
kernel space.
|
||||
@ -1669,9 +1669,19 @@ read(opfd, out, outlen);
|
||||
</chapter>
|
||||
|
||||
<chapter id="API"><title>Programming Interface</title>
|
||||
<para>
|
||||
Please note that the kernel crypto API contains the AEAD givcrypt
|
||||
API (crypto_aead_giv* and aead_givcrypt_* function calls in
|
||||
include/crypto/aead.h). This API is obsolete and will be removed
|
||||
in the future. To obtain the functionality of an AEAD cipher with
|
||||
internal IV generation, use the IV generator as a regular cipher.
|
||||
For example, rfc4106(gcm(aes)) is the AEAD cipher with external
|
||||
IV generation and seqniv(rfc4106(gcm(aes))) implies that the kernel
|
||||
crypto API generates the IV. Different IV generators are available.
|
||||
</para>
|
||||
<sect1><title>Block Cipher Context Data Structures</title>
|
||||
!Pinclude/linux/crypto.h Block Cipher Context Data Structures
|
||||
!Finclude/linux/crypto.h aead_request
|
||||
!Finclude/crypto/aead.h aead_request
|
||||
</sect1>
|
||||
<sect1><title>Block Cipher Algorithm Definitions</title>
|
||||
!Pinclude/linux/crypto.h Block Cipher Algorithm Definitions
|
||||
@ -1680,7 +1690,7 @@ read(opfd, out, outlen);
|
||||
!Finclude/linux/crypto.h aead_alg
|
||||
!Finclude/linux/crypto.h blkcipher_alg
|
||||
!Finclude/linux/crypto.h cipher_alg
|
||||
!Finclude/linux/crypto.h rng_alg
|
||||
!Finclude/crypto/rng.h rng_alg
|
||||
</sect1>
|
||||
<sect1><title>Asynchronous Block Cipher API</title>
|
||||
!Pinclude/linux/crypto.h Asynchronous Block Cipher API
|
||||
@ -1704,26 +1714,27 @@ read(opfd, out, outlen);
|
||||
!Finclude/linux/crypto.h ablkcipher_request_set_crypt
|
||||
</sect1>
|
||||
<sect1><title>Authenticated Encryption With Associated Data (AEAD) Cipher API</title>
|
||||
!Pinclude/linux/crypto.h Authenticated Encryption With Associated Data (AEAD) Cipher API
|
||||
!Finclude/linux/crypto.h crypto_alloc_aead
|
||||
!Finclude/linux/crypto.h crypto_free_aead
|
||||
!Finclude/linux/crypto.h crypto_aead_ivsize
|
||||
!Finclude/linux/crypto.h crypto_aead_authsize
|
||||
!Finclude/linux/crypto.h crypto_aead_blocksize
|
||||
!Finclude/linux/crypto.h crypto_aead_setkey
|
||||
!Finclude/linux/crypto.h crypto_aead_setauthsize
|
||||
!Finclude/linux/crypto.h crypto_aead_encrypt
|
||||
!Finclude/linux/crypto.h crypto_aead_decrypt
|
||||
!Pinclude/crypto/aead.h Authenticated Encryption With Associated Data (AEAD) Cipher API
|
||||
!Finclude/crypto/aead.h crypto_alloc_aead
|
||||
!Finclude/crypto/aead.h crypto_free_aead
|
||||
!Finclude/crypto/aead.h crypto_aead_ivsize
|
||||
!Finclude/crypto/aead.h crypto_aead_authsize
|
||||
!Finclude/crypto/aead.h crypto_aead_blocksize
|
||||
!Finclude/crypto/aead.h crypto_aead_setkey
|
||||
!Finclude/crypto/aead.h crypto_aead_setauthsize
|
||||
!Finclude/crypto/aead.h crypto_aead_encrypt
|
||||
!Finclude/crypto/aead.h crypto_aead_decrypt
|
||||
</sect1>
|
||||
<sect1><title>Asynchronous AEAD Request Handle</title>
|
||||
!Pinclude/linux/crypto.h Asynchronous AEAD Request Handle
|
||||
!Finclude/linux/crypto.h crypto_aead_reqsize
|
||||
!Finclude/linux/crypto.h aead_request_set_tfm
|
||||
!Finclude/linux/crypto.h aead_request_alloc
|
||||
!Finclude/linux/crypto.h aead_request_free
|
||||
!Finclude/linux/crypto.h aead_request_set_callback
|
||||
!Finclude/linux/crypto.h aead_request_set_crypt
|
||||
!Finclude/linux/crypto.h aead_request_set_assoc
|
||||
!Pinclude/crypto/aead.h Asynchronous AEAD Request Handle
|
||||
!Finclude/crypto/aead.h crypto_aead_reqsize
|
||||
!Finclude/crypto/aead.h aead_request_set_tfm
|
||||
!Finclude/crypto/aead.h aead_request_alloc
|
||||
!Finclude/crypto/aead.h aead_request_free
|
||||
!Finclude/crypto/aead.h aead_request_set_callback
|
||||
!Finclude/crypto/aead.h aead_request_set_crypt
|
||||
!Finclude/crypto/aead.h aead_request_set_assoc
|
||||
!Finclude/crypto/aead.h aead_request_set_ad
|
||||
</sect1>
|
||||
<sect1><title>Synchronous Block Cipher API</title>
|
||||
!Pinclude/linux/crypto.h Synchronous Block Cipher API
|
||||
|
@ -1,9 +1,11 @@
|
||||
Freescale SoC SEC Security Engines versions 2.x-3.x
|
||||
Freescale SoC SEC Security Engines versions 1.x-2.x-3.x
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : Should contain entries for this and backward compatible
|
||||
SEC versions, high to low, e.g., "fsl,sec2.1", "fsl,sec2.0"
|
||||
SEC versions, high to low, e.g., "fsl,sec2.1", "fsl,sec2.0" (SEC2/3)
|
||||
e.g., "fsl,sec1.2", "fsl,sec1.0" (SEC1)
|
||||
warning: SEC1 and SEC2 are mutually exclusive
|
||||
- reg : Offset and length of the register set for the device
|
||||
- interrupts : the SEC's interrupt number
|
||||
- fsl,num-channels : An integer representing the number of channels
|
||||
|
45
Documentation/devicetree/bindings/crypto/marvell-cesa.txt
Normal file
45
Documentation/devicetree/bindings/crypto/marvell-cesa.txt
Normal file
@ -0,0 +1,45 @@
|
||||
Marvell Cryptographic Engines And Security Accelerator
|
||||
|
||||
Required properties:
|
||||
- compatible: should be one of the following string
|
||||
"marvell,orion-crypto"
|
||||
"marvell,kirkwood-crypto"
|
||||
"marvell,dove-crypto"
|
||||
"marvell,armada-370-crypto"
|
||||
"marvell,armada-xp-crypto"
|
||||
"marvell,armada-375-crypto"
|
||||
"marvell,armada-38x-crypto"
|
||||
- reg: base physical address of the engine and length of memory mapped
|
||||
region. Can also contain an entry for the SRAM attached to the CESA,
|
||||
but this representation is deprecated and marvell,crypto-srams should
|
||||
be used instead
|
||||
- reg-names: "regs". Can contain an "sram" entry, but this representation
|
||||
is deprecated and marvell,crypto-srams should be used instead
|
||||
- interrupts: interrupt number
|
||||
- clocks: reference to the crypto engines clocks. This property is not
|
||||
required for orion and kirkwood platforms
|
||||
- clock-names: "cesaX" and "cesazX", X should be replaced by the crypto engine
|
||||
id.
|
||||
This property is not required for the orion and kirkwoord
|
||||
platforms.
|
||||
"cesazX" clocks are not required on armada-370 platforms
|
||||
- marvell,crypto-srams: phandle to crypto SRAM definitions
|
||||
|
||||
Optional properties:
|
||||
- marvell,crypto-sram-size: SRAM size reserved for crypto operations, if not
|
||||
specified the whole SRAM is used (2KB)
|
||||
|
||||
|
||||
Examples:
|
||||
|
||||
crypto@90000 {
|
||||
compatible = "marvell,armada-xp-crypto";
|
||||
reg = <0x90000 0x10000>;
|
||||
reg-names = "regs";
|
||||
interrupts = <48>, <49>;
|
||||
clocks = <&gateclk 23>, <&gateclk 23>;
|
||||
clock-names = "cesa0", "cesa1";
|
||||
marvell,crypto-srams = <&crypto_sram0>, <&crypto_sram1>;
|
||||
marvell,crypto-sram-size = <0x600>;
|
||||
status = "okay";
|
||||
};
|
@ -1,20 +1,33 @@
|
||||
Marvell Cryptographic Engines And Security Accelerator
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "marvell,orion-crypto"
|
||||
- reg : base physical address of the engine and length of memory mapped
|
||||
region, followed by base physical address of sram and its memory
|
||||
length
|
||||
- reg-names : "regs" , "sram";
|
||||
- interrupts : interrupt number
|
||||
- compatible: should be one of the following string
|
||||
"marvell,orion-crypto"
|
||||
"marvell,kirkwood-crypto"
|
||||
"marvell,dove-crypto"
|
||||
- reg: base physical address of the engine and length of memory mapped
|
||||
region. Can also contain an entry for the SRAM attached to the CESA,
|
||||
but this representation is deprecated and marvell,crypto-srams should
|
||||
be used instead
|
||||
- reg-names: "regs". Can contain an "sram" entry, but this representation
|
||||
is deprecated and marvell,crypto-srams should be used instead
|
||||
- interrupts: interrupt number
|
||||
- clocks: reference to the crypto engines clocks. This property is only
|
||||
required for Dove platforms
|
||||
- marvell,crypto-srams: phandle to crypto SRAM definitions
|
||||
|
||||
Optional properties:
|
||||
- marvell,crypto-sram-size: SRAM size reserved for crypto operations, if not
|
||||
specified the whole SRAM is used (2KB)
|
||||
|
||||
Examples:
|
||||
|
||||
crypto@30000 {
|
||||
compatible = "marvell,orion-crypto";
|
||||
reg = <0x30000 0x10000>,
|
||||
<0x4000000 0x800>;
|
||||
reg-names = "regs" , "sram";
|
||||
reg = <0x30000 0x10000>;
|
||||
reg-names = "regs";
|
||||
interrupts = <22>;
|
||||
marvell,crypto-srams = <&crypto_sram>;
|
||||
marvell,crypto-sram-size = <0x600>;
|
||||
status = "okay";
|
||||
};
|
||||
|
16
MAINTAINERS
16
MAINTAINERS
@ -4879,13 +4879,23 @@ M: Marcelo Henrique Cerri <mhcerri@linux.vnet.ibm.com>
|
||||
M: Fionnuala Gunter <fin@linux.vnet.ibm.com>
|
||||
L: linux-crypto@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/crypto/nx/
|
||||
F: drivers/crypto/nx/Makefile
|
||||
F: drivers/crypto/nx/Kconfig
|
||||
F: drivers/crypto/nx/nx-aes*
|
||||
F: drivers/crypto/nx/nx-sha*
|
||||
F: drivers/crypto/nx/nx.*
|
||||
F: drivers/crypto/nx/nx_csbcpb.h
|
||||
F: drivers/crypto/nx/nx_debugfs.h
|
||||
|
||||
IBM Power 842 compression accelerator
|
||||
M: Dan Streetman <ddstreet@us.ibm.com>
|
||||
S: Supported
|
||||
F: drivers/crypto/nx/nx-842.c
|
||||
F: include/linux/nx842.h
|
||||
F: drivers/crypto/nx/Makefile
|
||||
F: drivers/crypto/nx/Kconfig
|
||||
F: drivers/crypto/nx/nx-842*
|
||||
F: include/linux/sw842.h
|
||||
F: crypto/842.c
|
||||
F: lib/842/
|
||||
|
||||
IBM Power Linux RAID adapter
|
||||
M: Brian King <brking@us.ibm.com>
|
||||
|
@ -53,20 +53,13 @@ config CRYPTO_SHA256_ARM
|
||||
SHA-256 secure hash standard (DFIPS 180-2) implemented
|
||||
using optimized ARM assembler and NEON, when available.
|
||||
|
||||
config CRYPTO_SHA512_ARM_NEON
|
||||
tristate "SHA384 and SHA512 digest algorithm (ARM NEON)"
|
||||
depends on KERNEL_MODE_NEON
|
||||
select CRYPTO_SHA512
|
||||
config CRYPTO_SHA512_ARM
|
||||
tristate "SHA-384/512 digest algorithm (ARM-asm and NEON)"
|
||||
select CRYPTO_HASH
|
||||
depends on !CPU_V7M
|
||||
help
|
||||
SHA-512 secure hash standard (DFIPS 180-2) implemented
|
||||
using ARM NEON instructions, when available.
|
||||
|
||||
This version of SHA implements a 512 bit hash with 256 bits of
|
||||
security against collision attacks.
|
||||
|
||||
This code also includes SHA-384, a 384 bit hash with 192 bits
|
||||
of security against collision attacks.
|
||||
using optimized ARM assembler and NEON, when available.
|
||||
|
||||
config CRYPTO_AES_ARM
|
||||
tristate "AES cipher algorithms (ARM-asm)"
|
||||
|
@ -7,7 +7,7 @@ obj-$(CONFIG_CRYPTO_AES_ARM_BS) += aes-arm-bs.o
|
||||
obj-$(CONFIG_CRYPTO_SHA1_ARM) += sha1-arm.o
|
||||
obj-$(CONFIG_CRYPTO_SHA1_ARM_NEON) += sha1-arm-neon.o
|
||||
obj-$(CONFIG_CRYPTO_SHA256_ARM) += sha256-arm.o
|
||||
obj-$(CONFIG_CRYPTO_SHA512_ARM_NEON) += sha512-arm-neon.o
|
||||
obj-$(CONFIG_CRYPTO_SHA512_ARM) += sha512-arm.o
|
||||
|
||||
ce-obj-$(CONFIG_CRYPTO_AES_ARM_CE) += aes-arm-ce.o
|
||||
ce-obj-$(CONFIG_CRYPTO_SHA1_ARM_CE) += sha1-arm-ce.o
|
||||
@ -30,7 +30,8 @@ sha1-arm-y := sha1-armv4-large.o sha1_glue.o
|
||||
sha1-arm-neon-y := sha1-armv7-neon.o sha1_neon_glue.o
|
||||
sha256-arm-neon-$(CONFIG_KERNEL_MODE_NEON) := sha256_neon_glue.o
|
||||
sha256-arm-y := sha256-core.o sha256_glue.o $(sha256-arm-neon-y)
|
||||
sha512-arm-neon-y := sha512-armv7-neon.o sha512_neon_glue.o
|
||||
sha512-arm-neon-$(CONFIG_KERNEL_MODE_NEON) := sha512-neon-glue.o
|
||||
sha512-arm-y := sha512-core.o sha512-glue.o $(sha512-arm-neon-y)
|
||||
sha1-arm-ce-y := sha1-ce-core.o sha1-ce-glue.o
|
||||
sha2-arm-ce-y := sha2-ce-core.o sha2-ce-glue.o
|
||||
aes-arm-ce-y := aes-ce-core.o aes-ce-glue.o
|
||||
@ -45,4 +46,7 @@ $(src)/aesbs-core.S_shipped: $(src)/bsaes-armv7.pl
|
||||
$(src)/sha256-core.S_shipped: $(src)/sha256-armv4.pl
|
||||
$(call cmd,perl)
|
||||
|
||||
.PRECIOUS: $(obj)/aesbs-core.S $(obj)/sha256-core.S
|
||||
$(src)/sha512-core.S_shipped: $(src)/sha512-armv4.pl
|
||||
$(call cmd,perl)
|
||||
|
||||
.PRECIOUS: $(obj)/aesbs-core.S $(obj)/sha256-core.S $(obj)/sha512-core.S
|
||||
|
@ -101,15 +101,14 @@
|
||||
\dround q10, q11
|
||||
blo 0f @ AES-128: 10 rounds
|
||||
vld1.8 {q10-q11}, [ip]!
|
||||
beq 1f @ AES-192: 12 rounds
|
||||
\dround q12, q13
|
||||
beq 1f @ AES-192: 12 rounds
|
||||
vld1.8 {q12-q13}, [ip]
|
||||
\dround q10, q11
|
||||
0: \fround q12, q13, q14
|
||||
bx lr
|
||||
|
||||
1: \dround q12, q13
|
||||
\fround q10, q11, q14
|
||||
1: \fround q10, q11, q14
|
||||
bx lr
|
||||
.endm
|
||||
|
||||
@ -122,8 +121,8 @@
|
||||
* q2 : third in/output block (_3x version only)
|
||||
* q8 : first round key
|
||||
* q9 : secound round key
|
||||
* ip : address of 3rd round key
|
||||
* q14 : final round key
|
||||
* r2 : address of round key array
|
||||
* r3 : number of rounds
|
||||
*/
|
||||
.align 6
|
||||
|
649
arch/arm/crypto/sha512-armv4.pl
Normal file
649
arch/arm/crypto/sha512-armv4.pl
Normal file
@ -0,0 +1,649 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
# ====================================================================
|
||||
# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
|
||||
# project. The module is, however, dual licensed under OpenSSL and
|
||||
# CRYPTOGAMS licenses depending on where you obtain it. For further
|
||||
# details see http://www.openssl.org/~appro/cryptogams/.
|
||||
#
|
||||
# Permission to use under GPL terms is granted.
|
||||
# ====================================================================
|
||||
|
||||
# SHA512 block procedure for ARMv4. September 2007.
|
||||
|
||||
# This code is ~4.5 (four and a half) times faster than code generated
|
||||
# by gcc 3.4 and it spends ~72 clock cycles per byte [on single-issue
|
||||
# Xscale PXA250 core].
|
||||
#
|
||||
# July 2010.
|
||||
#
|
||||
# Rescheduling for dual-issue pipeline resulted in 6% improvement on
|
||||
# Cortex A8 core and ~40 cycles per processed byte.
|
||||
|
||||
# February 2011.
|
||||
#
|
||||
# Profiler-assisted and platform-specific optimization resulted in 7%
|
||||
# improvement on Coxtex A8 core and ~38 cycles per byte.
|
||||
|
||||
# March 2011.
|
||||
#
|
||||
# Add NEON implementation. On Cortex A8 it was measured to process
|
||||
# one byte in 23.3 cycles or ~60% faster than integer-only code.
|
||||
|
||||
# August 2012.
|
||||
#
|
||||
# Improve NEON performance by 12% on Snapdragon S4. In absolute
|
||||
# terms it's 22.6 cycles per byte, which is disappointing result.
|
||||
# Technical writers asserted that 3-way S4 pipeline can sustain
|
||||
# multiple NEON instructions per cycle, but dual NEON issue could
|
||||
# not be observed, see http://www.openssl.org/~appro/Snapdragon-S4.html
|
||||
# for further details. On side note Cortex-A15 processes one byte in
|
||||
# 16 cycles.
|
||||
|
||||
# Byte order [in]dependence. =========================================
|
||||
#
|
||||
# Originally caller was expected to maintain specific *dword* order in
|
||||
# h[0-7], namely with most significant dword at *lower* address, which
|
||||
# was reflected in below two parameters as 0 and 4. Now caller is
|
||||
# expected to maintain native byte order for whole 64-bit values.
|
||||
$hi="HI";
|
||||
$lo="LO";
|
||||
# ====================================================================
|
||||
|
||||
while (($output=shift) && ($output!~/^\w[\w\-]*\.\w+$/)) {}
|
||||
open STDOUT,">$output";
|
||||
|
||||
$ctx="r0"; # parameter block
|
||||
$inp="r1";
|
||||
$len="r2";
|
||||
|
||||
$Tlo="r3";
|
||||
$Thi="r4";
|
||||
$Alo="r5";
|
||||
$Ahi="r6";
|
||||
$Elo="r7";
|
||||
$Ehi="r8";
|
||||
$t0="r9";
|
||||
$t1="r10";
|
||||
$t2="r11";
|
||||
$t3="r12";
|
||||
############ r13 is stack pointer
|
||||
$Ktbl="r14";
|
||||
############ r15 is program counter
|
||||
|
||||
$Aoff=8*0;
|
||||
$Boff=8*1;
|
||||
$Coff=8*2;
|
||||
$Doff=8*3;
|
||||
$Eoff=8*4;
|
||||
$Foff=8*5;
|
||||
$Goff=8*6;
|
||||
$Hoff=8*7;
|
||||
$Xoff=8*8;
|
||||
|
||||
sub BODY_00_15() {
|
||||
my $magic = shift;
|
||||
$code.=<<___;
|
||||
@ Sigma1(x) (ROTR((x),14) ^ ROTR((x),18) ^ ROTR((x),41))
|
||||
@ LO lo>>14^hi<<18 ^ lo>>18^hi<<14 ^ hi>>9^lo<<23
|
||||
@ HI hi>>14^lo<<18 ^ hi>>18^lo<<14 ^ lo>>9^hi<<23
|
||||
mov $t0,$Elo,lsr#14
|
||||
str $Tlo,[sp,#$Xoff+0]
|
||||
mov $t1,$Ehi,lsr#14
|
||||
str $Thi,[sp,#$Xoff+4]
|
||||
eor $t0,$t0,$Ehi,lsl#18
|
||||
ldr $t2,[sp,#$Hoff+0] @ h.lo
|
||||
eor $t1,$t1,$Elo,lsl#18
|
||||
ldr $t3,[sp,#$Hoff+4] @ h.hi
|
||||
eor $t0,$t0,$Elo,lsr#18
|
||||
eor $t1,$t1,$Ehi,lsr#18
|
||||
eor $t0,$t0,$Ehi,lsl#14
|
||||
eor $t1,$t1,$Elo,lsl#14
|
||||
eor $t0,$t0,$Ehi,lsr#9
|
||||
eor $t1,$t1,$Elo,lsr#9
|
||||
eor $t0,$t0,$Elo,lsl#23
|
||||
eor $t1,$t1,$Ehi,lsl#23 @ Sigma1(e)
|
||||
adds $Tlo,$Tlo,$t0
|
||||
ldr $t0,[sp,#$Foff+0] @ f.lo
|
||||
adc $Thi,$Thi,$t1 @ T += Sigma1(e)
|
||||
ldr $t1,[sp,#$Foff+4] @ f.hi
|
||||
adds $Tlo,$Tlo,$t2
|
||||
ldr $t2,[sp,#$Goff+0] @ g.lo
|
||||
adc $Thi,$Thi,$t3 @ T += h
|
||||
ldr $t3,[sp,#$Goff+4] @ g.hi
|
||||
|
||||
eor $t0,$t0,$t2
|
||||
str $Elo,[sp,#$Eoff+0]
|
||||
eor $t1,$t1,$t3
|
||||
str $Ehi,[sp,#$Eoff+4]
|
||||
and $t0,$t0,$Elo
|
||||
str $Alo,[sp,#$Aoff+0]
|
||||
and $t1,$t1,$Ehi
|
||||
str $Ahi,[sp,#$Aoff+4]
|
||||
eor $t0,$t0,$t2
|
||||
ldr $t2,[$Ktbl,#$lo] @ K[i].lo
|
||||
eor $t1,$t1,$t3 @ Ch(e,f,g)
|
||||
ldr $t3,[$Ktbl,#$hi] @ K[i].hi
|
||||
|
||||
adds $Tlo,$Tlo,$t0
|
||||
ldr $Elo,[sp,#$Doff+0] @ d.lo
|
||||
adc $Thi,$Thi,$t1 @ T += Ch(e,f,g)
|
||||
ldr $Ehi,[sp,#$Doff+4] @ d.hi
|
||||
adds $Tlo,$Tlo,$t2
|
||||
and $t0,$t2,#0xff
|
||||
adc $Thi,$Thi,$t3 @ T += K[i]
|
||||
adds $Elo,$Elo,$Tlo
|
||||
ldr $t2,[sp,#$Boff+0] @ b.lo
|
||||
adc $Ehi,$Ehi,$Thi @ d += T
|
||||
teq $t0,#$magic
|
||||
|
||||
ldr $t3,[sp,#$Coff+0] @ c.lo
|
||||
#if __ARM_ARCH__>=7
|
||||
it eq @ Thumb2 thing, sanity check in ARM
|
||||
#endif
|
||||
orreq $Ktbl,$Ktbl,#1
|
||||
@ Sigma0(x) (ROTR((x),28) ^ ROTR((x),34) ^ ROTR((x),39))
|
||||
@ LO lo>>28^hi<<4 ^ hi>>2^lo<<30 ^ hi>>7^lo<<25
|
||||
@ HI hi>>28^lo<<4 ^ lo>>2^hi<<30 ^ lo>>7^hi<<25
|
||||
mov $t0,$Alo,lsr#28
|
||||
mov $t1,$Ahi,lsr#28
|
||||
eor $t0,$t0,$Ahi,lsl#4
|
||||
eor $t1,$t1,$Alo,lsl#4
|
||||
eor $t0,$t0,$Ahi,lsr#2
|
||||
eor $t1,$t1,$Alo,lsr#2
|
||||
eor $t0,$t0,$Alo,lsl#30
|
||||
eor $t1,$t1,$Ahi,lsl#30
|
||||
eor $t0,$t0,$Ahi,lsr#7
|
||||
eor $t1,$t1,$Alo,lsr#7
|
||||
eor $t0,$t0,$Alo,lsl#25
|
||||
eor $t1,$t1,$Ahi,lsl#25 @ Sigma0(a)
|
||||
adds $Tlo,$Tlo,$t0
|
||||
and $t0,$Alo,$t2
|
||||
adc $Thi,$Thi,$t1 @ T += Sigma0(a)
|
||||
|
||||
ldr $t1,[sp,#$Boff+4] @ b.hi
|
||||
orr $Alo,$Alo,$t2
|
||||
ldr $t2,[sp,#$Coff+4] @ c.hi
|
||||
and $Alo,$Alo,$t3
|
||||
and $t3,$Ahi,$t1
|
||||
orr $Ahi,$Ahi,$t1
|
||||
orr $Alo,$Alo,$t0 @ Maj(a,b,c).lo
|
||||
and $Ahi,$Ahi,$t2
|
||||
adds $Alo,$Alo,$Tlo
|
||||
orr $Ahi,$Ahi,$t3 @ Maj(a,b,c).hi
|
||||
sub sp,sp,#8
|
||||
adc $Ahi,$Ahi,$Thi @ h += T
|
||||
tst $Ktbl,#1
|
||||
add $Ktbl,$Ktbl,#8
|
||||
___
|
||||
}
|
||||
$code=<<___;
|
||||
#ifndef __KERNEL__
|
||||
# include "arm_arch.h"
|
||||
# define VFP_ABI_PUSH vstmdb sp!,{d8-d15}
|
||||
# define VFP_ABI_POP vldmia sp!,{d8-d15}
|
||||
#else
|
||||
# define __ARM_ARCH__ __LINUX_ARM_ARCH__
|
||||
# define __ARM_MAX_ARCH__ 7
|
||||
# define VFP_ABI_PUSH
|
||||
# define VFP_ABI_POP
|
||||
#endif
|
||||
|
||||
#ifdef __ARMEL__
|
||||
# define LO 0
|
||||
# define HI 4
|
||||
# define WORD64(hi0,lo0,hi1,lo1) .word lo0,hi0, lo1,hi1
|
||||
#else
|
||||
# define HI 0
|
||||
# define LO 4
|
||||
# define WORD64(hi0,lo0,hi1,lo1) .word hi0,lo0, hi1,lo1
|
||||
#endif
|
||||
|
||||
.text
|
||||
#if __ARM_ARCH__<7
|
||||
.code 32
|
||||
#else
|
||||
.syntax unified
|
||||
# ifdef __thumb2__
|
||||
# define adrl adr
|
||||
.thumb
|
||||
# else
|
||||
.code 32
|
||||
# endif
|
||||
#endif
|
||||
|
||||
.type K512,%object
|
||||
.align 5
|
||||
K512:
|
||||
WORD64(0x428a2f98,0xd728ae22, 0x71374491,0x23ef65cd)
|
||||
WORD64(0xb5c0fbcf,0xec4d3b2f, 0xe9b5dba5,0x8189dbbc)
|
||||
WORD64(0x3956c25b,0xf348b538, 0x59f111f1,0xb605d019)
|
||||
WORD64(0x923f82a4,0xaf194f9b, 0xab1c5ed5,0xda6d8118)
|
||||
WORD64(0xd807aa98,0xa3030242, 0x12835b01,0x45706fbe)
|
||||
WORD64(0x243185be,0x4ee4b28c, 0x550c7dc3,0xd5ffb4e2)
|
||||
WORD64(0x72be5d74,0xf27b896f, 0x80deb1fe,0x3b1696b1)
|
||||
WORD64(0x9bdc06a7,0x25c71235, 0xc19bf174,0xcf692694)
|
||||
WORD64(0xe49b69c1,0x9ef14ad2, 0xefbe4786,0x384f25e3)
|
||||
WORD64(0x0fc19dc6,0x8b8cd5b5, 0x240ca1cc,0x77ac9c65)
|
||||
WORD64(0x2de92c6f,0x592b0275, 0x4a7484aa,0x6ea6e483)
|
||||
WORD64(0x5cb0a9dc,0xbd41fbd4, 0x76f988da,0x831153b5)
|
||||
WORD64(0x983e5152,0xee66dfab, 0xa831c66d,0x2db43210)
|
||||
WORD64(0xb00327c8,0x98fb213f, 0xbf597fc7,0xbeef0ee4)
|
||||
WORD64(0xc6e00bf3,0x3da88fc2, 0xd5a79147,0x930aa725)
|
||||
WORD64(0x06ca6351,0xe003826f, 0x14292967,0x0a0e6e70)
|
||||
WORD64(0x27b70a85,0x46d22ffc, 0x2e1b2138,0x5c26c926)
|
||||
WORD64(0x4d2c6dfc,0x5ac42aed, 0x53380d13,0x9d95b3df)
|
||||
WORD64(0x650a7354,0x8baf63de, 0x766a0abb,0x3c77b2a8)
|
||||
WORD64(0x81c2c92e,0x47edaee6, 0x92722c85,0x1482353b)
|
||||
WORD64(0xa2bfe8a1,0x4cf10364, 0xa81a664b,0xbc423001)
|
||||
WORD64(0xc24b8b70,0xd0f89791, 0xc76c51a3,0x0654be30)
|
||||
WORD64(0xd192e819,0xd6ef5218, 0xd6990624,0x5565a910)
|
||||
WORD64(0xf40e3585,0x5771202a, 0x106aa070,0x32bbd1b8)
|
||||
WORD64(0x19a4c116,0xb8d2d0c8, 0x1e376c08,0x5141ab53)
|
||||
WORD64(0x2748774c,0xdf8eeb99, 0x34b0bcb5,0xe19b48a8)
|
||||
WORD64(0x391c0cb3,0xc5c95a63, 0x4ed8aa4a,0xe3418acb)
|
||||
WORD64(0x5b9cca4f,0x7763e373, 0x682e6ff3,0xd6b2b8a3)
|
||||
WORD64(0x748f82ee,0x5defb2fc, 0x78a5636f,0x43172f60)
|
||||
WORD64(0x84c87814,0xa1f0ab72, 0x8cc70208,0x1a6439ec)
|
||||
WORD64(0x90befffa,0x23631e28, 0xa4506ceb,0xde82bde9)
|
||||
WORD64(0xbef9a3f7,0xb2c67915, 0xc67178f2,0xe372532b)
|
||||
WORD64(0xca273ece,0xea26619c, 0xd186b8c7,0x21c0c207)
|
||||
WORD64(0xeada7dd6,0xcde0eb1e, 0xf57d4f7f,0xee6ed178)
|
||||
WORD64(0x06f067aa,0x72176fba, 0x0a637dc5,0xa2c898a6)
|
||||
WORD64(0x113f9804,0xbef90dae, 0x1b710b35,0x131c471b)
|
||||
WORD64(0x28db77f5,0x23047d84, 0x32caab7b,0x40c72493)
|
||||
WORD64(0x3c9ebe0a,0x15c9bebc, 0x431d67c4,0x9c100d4c)
|
||||
WORD64(0x4cc5d4be,0xcb3e42b6, 0x597f299c,0xfc657e2a)
|
||||
WORD64(0x5fcb6fab,0x3ad6faec, 0x6c44198c,0x4a475817)
|
||||
.size K512,.-K512
|
||||
#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
|
||||
.LOPENSSL_armcap:
|
||||
.word OPENSSL_armcap_P-sha512_block_data_order
|
||||
.skip 32-4
|
||||
#else
|
||||
.skip 32
|
||||
#endif
|
||||
|
||||
.global sha512_block_data_order
|
||||
.type sha512_block_data_order,%function
|
||||
sha512_block_data_order:
|
||||
#if __ARM_ARCH__<7
|
||||
sub r3,pc,#8 @ sha512_block_data_order
|
||||
#else
|
||||
adr r3,sha512_block_data_order
|
||||
#endif
|
||||
#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
|
||||
ldr r12,.LOPENSSL_armcap
|
||||
ldr r12,[r3,r12] @ OPENSSL_armcap_P
|
||||
tst r12,#1
|
||||
bne .LNEON
|
||||
#endif
|
||||
add $len,$inp,$len,lsl#7 @ len to point at the end of inp
|
||||
stmdb sp!,{r4-r12,lr}
|
||||
sub $Ktbl,r3,#672 @ K512
|
||||
sub sp,sp,#9*8
|
||||
|
||||
ldr $Elo,[$ctx,#$Eoff+$lo]
|
||||
ldr $Ehi,[$ctx,#$Eoff+$hi]
|
||||
ldr $t0, [$ctx,#$Goff+$lo]
|
||||
ldr $t1, [$ctx,#$Goff+$hi]
|
||||
ldr $t2, [$ctx,#$Hoff+$lo]
|
||||
ldr $t3, [$ctx,#$Hoff+$hi]
|
||||
.Loop:
|
||||
str $t0, [sp,#$Goff+0]
|
||||
str $t1, [sp,#$Goff+4]
|
||||
str $t2, [sp,#$Hoff+0]
|
||||
str $t3, [sp,#$Hoff+4]
|
||||
ldr $Alo,[$ctx,#$Aoff+$lo]
|
||||
ldr $Ahi,[$ctx,#$Aoff+$hi]
|
||||
ldr $Tlo,[$ctx,#$Boff+$lo]
|
||||
ldr $Thi,[$ctx,#$Boff+$hi]
|
||||
ldr $t0, [$ctx,#$Coff+$lo]
|
||||
ldr $t1, [$ctx,#$Coff+$hi]
|
||||
ldr $t2, [$ctx,#$Doff+$lo]
|
||||
ldr $t3, [$ctx,#$Doff+$hi]
|
||||
str $Tlo,[sp,#$Boff+0]
|
||||
str $Thi,[sp,#$Boff+4]
|
||||
str $t0, [sp,#$Coff+0]
|
||||
str $t1, [sp,#$Coff+4]
|
||||
str $t2, [sp,#$Doff+0]
|
||||
str $t3, [sp,#$Doff+4]
|
||||
ldr $Tlo,[$ctx,#$Foff+$lo]
|
||||
ldr $Thi,[$ctx,#$Foff+$hi]
|
||||
str $Tlo,[sp,#$Foff+0]
|
||||
str $Thi,[sp,#$Foff+4]
|
||||
|
||||
.L00_15:
|
||||
#if __ARM_ARCH__<7
|
||||
ldrb $Tlo,[$inp,#7]
|
||||
ldrb $t0, [$inp,#6]
|
||||
ldrb $t1, [$inp,#5]
|
||||
ldrb $t2, [$inp,#4]
|
||||
ldrb $Thi,[$inp,#3]
|
||||
ldrb $t3, [$inp,#2]
|
||||
orr $Tlo,$Tlo,$t0,lsl#8
|
||||
ldrb $t0, [$inp,#1]
|
||||
orr $Tlo,$Tlo,$t1,lsl#16
|
||||
ldrb $t1, [$inp],#8
|
||||
orr $Tlo,$Tlo,$t2,lsl#24
|
||||
orr $Thi,$Thi,$t3,lsl#8
|
||||
orr $Thi,$Thi,$t0,lsl#16
|
||||
orr $Thi,$Thi,$t1,lsl#24
|
||||
#else
|
||||
ldr $Tlo,[$inp,#4]
|
||||
ldr $Thi,[$inp],#8
|
||||
#ifdef __ARMEL__
|
||||
rev $Tlo,$Tlo
|
||||
rev $Thi,$Thi
|
||||
#endif
|
||||
#endif
|
||||
___
|
||||
&BODY_00_15(0x94);
|
||||
$code.=<<___;
|
||||
tst $Ktbl,#1
|
||||
beq .L00_15
|
||||
ldr $t0,[sp,#`$Xoff+8*(16-1)`+0]
|
||||
ldr $t1,[sp,#`$Xoff+8*(16-1)`+4]
|
||||
bic $Ktbl,$Ktbl,#1
|
||||
.L16_79:
|
||||
@ sigma0(x) (ROTR((x),1) ^ ROTR((x),8) ^ ((x)>>7))
|
||||
@ LO lo>>1^hi<<31 ^ lo>>8^hi<<24 ^ lo>>7^hi<<25
|
||||
@ HI hi>>1^lo<<31 ^ hi>>8^lo<<24 ^ hi>>7
|
||||
mov $Tlo,$t0,lsr#1
|
||||
ldr $t2,[sp,#`$Xoff+8*(16-14)`+0]
|
||||
mov $Thi,$t1,lsr#1
|
||||
ldr $t3,[sp,#`$Xoff+8*(16-14)`+4]
|
||||
eor $Tlo,$Tlo,$t1,lsl#31
|
||||
eor $Thi,$Thi,$t0,lsl#31
|
||||
eor $Tlo,$Tlo,$t0,lsr#8
|
||||
eor $Thi,$Thi,$t1,lsr#8
|
||||
eor $Tlo,$Tlo,$t1,lsl#24
|
||||
eor $Thi,$Thi,$t0,lsl#24
|
||||
eor $Tlo,$Tlo,$t0,lsr#7
|
||||
eor $Thi,$Thi,$t1,lsr#7
|
||||
eor $Tlo,$Tlo,$t1,lsl#25
|
||||
|
||||
@ sigma1(x) (ROTR((x),19) ^ ROTR((x),61) ^ ((x)>>6))
|
||||
@ LO lo>>19^hi<<13 ^ hi>>29^lo<<3 ^ lo>>6^hi<<26
|
||||
@ HI hi>>19^lo<<13 ^ lo>>29^hi<<3 ^ hi>>6
|
||||
mov $t0,$t2,lsr#19
|
||||
mov $t1,$t3,lsr#19
|
||||
eor $t0,$t0,$t3,lsl#13
|
||||
eor $t1,$t1,$t2,lsl#13
|
||||
eor $t0,$t0,$t3,lsr#29
|
||||
eor $t1,$t1,$t2,lsr#29
|
||||
eor $t0,$t0,$t2,lsl#3
|
||||
eor $t1,$t1,$t3,lsl#3
|
||||
eor $t0,$t0,$t2,lsr#6
|
||||
eor $t1,$t1,$t3,lsr#6
|
||||
ldr $t2,[sp,#`$Xoff+8*(16-9)`+0]
|
||||
eor $t0,$t0,$t3,lsl#26
|
||||
|
||||
ldr $t3,[sp,#`$Xoff+8*(16-9)`+4]
|
||||
adds $Tlo,$Tlo,$t0
|
||||
ldr $t0,[sp,#`$Xoff+8*16`+0]
|
||||
adc $Thi,$Thi,$t1
|
||||
|
||||
ldr $t1,[sp,#`$Xoff+8*16`+4]
|
||||
adds $Tlo,$Tlo,$t2
|
||||
adc $Thi,$Thi,$t3
|
||||
adds $Tlo,$Tlo,$t0
|
||||
adc $Thi,$Thi,$t1
|
||||
___
|
||||
&BODY_00_15(0x17);
|
||||
$code.=<<___;
|
||||
#if __ARM_ARCH__>=7
|
||||
ittt eq @ Thumb2 thing, sanity check in ARM
|
||||
#endif
|
||||
ldreq $t0,[sp,#`$Xoff+8*(16-1)`+0]
|
||||
ldreq $t1,[sp,#`$Xoff+8*(16-1)`+4]
|
||||
beq .L16_79
|
||||
bic $Ktbl,$Ktbl,#1
|
||||
|
||||
ldr $Tlo,[sp,#$Boff+0]
|
||||
ldr $Thi,[sp,#$Boff+4]
|
||||
ldr $t0, [$ctx,#$Aoff+$lo]
|
||||
ldr $t1, [$ctx,#$Aoff+$hi]
|
||||
ldr $t2, [$ctx,#$Boff+$lo]
|
||||
ldr $t3, [$ctx,#$Boff+$hi]
|
||||
adds $t0,$Alo,$t0
|
||||
str $t0, [$ctx,#$Aoff+$lo]
|
||||
adc $t1,$Ahi,$t1
|
||||
str $t1, [$ctx,#$Aoff+$hi]
|
||||
adds $t2,$Tlo,$t2
|
||||
str $t2, [$ctx,#$Boff+$lo]
|
||||
adc $t3,$Thi,$t3
|
||||
str $t3, [$ctx,#$Boff+$hi]
|
||||
|
||||
ldr $Alo,[sp,#$Coff+0]
|
||||
ldr $Ahi,[sp,#$Coff+4]
|
||||
ldr $Tlo,[sp,#$Doff+0]
|
||||
ldr $Thi,[sp,#$Doff+4]
|
||||
ldr $t0, [$ctx,#$Coff+$lo]
|
||||
ldr $t1, [$ctx,#$Coff+$hi]
|
||||
ldr $t2, [$ctx,#$Doff+$lo]
|
||||
ldr $t3, [$ctx,#$Doff+$hi]
|
||||
adds $t0,$Alo,$t0
|
||||
str $t0, [$ctx,#$Coff+$lo]
|
||||
adc $t1,$Ahi,$t1
|
||||
str $t1, [$ctx,#$Coff+$hi]
|
||||
adds $t2,$Tlo,$t2
|
||||
str $t2, [$ctx,#$Doff+$lo]
|
||||
adc $t3,$Thi,$t3
|
||||
str $t3, [$ctx,#$Doff+$hi]
|
||||
|
||||
ldr $Tlo,[sp,#$Foff+0]
|
||||
ldr $Thi,[sp,#$Foff+4]
|
||||
ldr $t0, [$ctx,#$Eoff+$lo]
|
||||
ldr $t1, [$ctx,#$Eoff+$hi]
|
||||
ldr $t2, [$ctx,#$Foff+$lo]
|
||||
ldr $t3, [$ctx,#$Foff+$hi]
|
||||
adds $Elo,$Elo,$t0
|
||||
str $Elo,[$ctx,#$Eoff+$lo]
|
||||
adc $Ehi,$Ehi,$t1
|
||||
str $Ehi,[$ctx,#$Eoff+$hi]
|
||||
adds $t2,$Tlo,$t2
|
||||
str $t2, [$ctx,#$Foff+$lo]
|
||||
adc $t3,$Thi,$t3
|
||||
str $t3, [$ctx,#$Foff+$hi]
|
||||
|
||||
ldr $Alo,[sp,#$Goff+0]
|
||||
ldr $Ahi,[sp,#$Goff+4]
|
||||
ldr $Tlo,[sp,#$Hoff+0]
|
||||
ldr $Thi,[sp,#$Hoff+4]
|
||||
ldr $t0, [$ctx,#$Goff+$lo]
|
||||
ldr $t1, [$ctx,#$Goff+$hi]
|
||||
ldr $t2, [$ctx,#$Hoff+$lo]
|
||||
ldr $t3, [$ctx,#$Hoff+$hi]
|
||||
adds $t0,$Alo,$t0
|
||||
str $t0, [$ctx,#$Goff+$lo]
|
||||
adc $t1,$Ahi,$t1
|
||||
str $t1, [$ctx,#$Goff+$hi]
|
||||
adds $t2,$Tlo,$t2
|
||||
str $t2, [$ctx,#$Hoff+$lo]
|
||||
adc $t3,$Thi,$t3
|
||||
str $t3, [$ctx,#$Hoff+$hi]
|
||||
|
||||
add sp,sp,#640
|
||||
sub $Ktbl,$Ktbl,#640
|
||||
|
||||
teq $inp,$len
|
||||
bne .Loop
|
||||
|
||||
add sp,sp,#8*9 @ destroy frame
|
||||
#if __ARM_ARCH__>=5
|
||||
ldmia sp!,{r4-r12,pc}
|
||||
#else
|
||||
ldmia sp!,{r4-r12,lr}
|
||||
tst lr,#1
|
||||
moveq pc,lr @ be binary compatible with V4, yet
|
||||
bx lr @ interoperable with Thumb ISA:-)
|
||||
#endif
|
||||
.size sha512_block_data_order,.-sha512_block_data_order
|
||||
___
|
||||
|
||||
{
|
||||
my @Sigma0=(28,34,39);
|
||||
my @Sigma1=(14,18,41);
|
||||
my @sigma0=(1, 8, 7);
|
||||
my @sigma1=(19,61,6);
|
||||
|
||||
my $Ktbl="r3";
|
||||
my $cnt="r12"; # volatile register known as ip, intra-procedure-call scratch
|
||||
|
||||
my @X=map("d$_",(0..15));
|
||||
my @V=($A,$B,$C,$D,$E,$F,$G,$H)=map("d$_",(16..23));
|
||||
|
||||
sub NEON_00_15() {
|
||||
my $i=shift;
|
||||
my ($a,$b,$c,$d,$e,$f,$g,$h)=@_;
|
||||
my ($t0,$t1,$t2,$T1,$K,$Ch,$Maj)=map("d$_",(24..31)); # temps
|
||||
|
||||
$code.=<<___ if ($i<16 || $i&1);
|
||||
vshr.u64 $t0,$e,#@Sigma1[0] @ $i
|
||||
#if $i<16
|
||||
vld1.64 {@X[$i%16]},[$inp]! @ handles unaligned
|
||||
#endif
|
||||
vshr.u64 $t1,$e,#@Sigma1[1]
|
||||
#if $i>0
|
||||
vadd.i64 $a,$Maj @ h+=Maj from the past
|
||||
#endif
|
||||
vshr.u64 $t2,$e,#@Sigma1[2]
|
||||
___
|
||||
$code.=<<___;
|
||||
vld1.64 {$K},[$Ktbl,:64]! @ K[i++]
|
||||
vsli.64 $t0,$e,#`64-@Sigma1[0]`
|
||||
vsli.64 $t1,$e,#`64-@Sigma1[1]`
|
||||
vmov $Ch,$e
|
||||
vsli.64 $t2,$e,#`64-@Sigma1[2]`
|
||||
#if $i<16 && defined(__ARMEL__)
|
||||
vrev64.8 @X[$i],@X[$i]
|
||||
#endif
|
||||
veor $t1,$t0
|
||||
vbsl $Ch,$f,$g @ Ch(e,f,g)
|
||||
vshr.u64 $t0,$a,#@Sigma0[0]
|
||||
veor $t2,$t1 @ Sigma1(e)
|
||||
vadd.i64 $T1,$Ch,$h
|
||||
vshr.u64 $t1,$a,#@Sigma0[1]
|
||||
vsli.64 $t0,$a,#`64-@Sigma0[0]`
|
||||
vadd.i64 $T1,$t2
|
||||
vshr.u64 $t2,$a,#@Sigma0[2]
|
||||
vadd.i64 $K,@X[$i%16]
|
||||
vsli.64 $t1,$a,#`64-@Sigma0[1]`
|
||||
veor $Maj,$a,$b
|
||||
vsli.64 $t2,$a,#`64-@Sigma0[2]`
|
||||
veor $h,$t0,$t1
|
||||
vadd.i64 $T1,$K
|
||||
vbsl $Maj,$c,$b @ Maj(a,b,c)
|
||||
veor $h,$t2 @ Sigma0(a)
|
||||
vadd.i64 $d,$T1
|
||||
vadd.i64 $Maj,$T1
|
||||
@ vadd.i64 $h,$Maj
|
||||
___
|
||||
}
|
||||
|
||||
sub NEON_16_79() {
|
||||
my $i=shift;
|
||||
|
||||
if ($i&1) { &NEON_00_15($i,@_); return; }
|
||||
|
||||
# 2x-vectorized, therefore runs every 2nd round
|
||||
my @X=map("q$_",(0..7)); # view @X as 128-bit vector
|
||||
my ($t0,$t1,$s0,$s1) = map("q$_",(12..15)); # temps
|
||||
my ($d0,$d1,$d2) = map("d$_",(24..26)); # temps from NEON_00_15
|
||||
my $e=@_[4]; # $e from NEON_00_15
|
||||
$i /= 2;
|
||||
$code.=<<___;
|
||||
vshr.u64 $t0,@X[($i+7)%8],#@sigma1[0]
|
||||
vshr.u64 $t1,@X[($i+7)%8],#@sigma1[1]
|
||||
vadd.i64 @_[0],d30 @ h+=Maj from the past
|
||||
vshr.u64 $s1,@X[($i+7)%8],#@sigma1[2]
|
||||
vsli.64 $t0,@X[($i+7)%8],#`64-@sigma1[0]`
|
||||
vext.8 $s0,@X[$i%8],@X[($i+1)%8],#8 @ X[i+1]
|
||||
vsli.64 $t1,@X[($i+7)%8],#`64-@sigma1[1]`
|
||||
veor $s1,$t0
|
||||
vshr.u64 $t0,$s0,#@sigma0[0]
|
||||
veor $s1,$t1 @ sigma1(X[i+14])
|
||||
vshr.u64 $t1,$s0,#@sigma0[1]
|
||||
vadd.i64 @X[$i%8],$s1
|
||||
vshr.u64 $s1,$s0,#@sigma0[2]
|
||||
vsli.64 $t0,$s0,#`64-@sigma0[0]`
|
||||
vsli.64 $t1,$s0,#`64-@sigma0[1]`
|
||||
vext.8 $s0,@X[($i+4)%8],@X[($i+5)%8],#8 @ X[i+9]
|
||||
veor $s1,$t0
|
||||
vshr.u64 $d0,$e,#@Sigma1[0] @ from NEON_00_15
|
||||
vadd.i64 @X[$i%8],$s0
|
||||
vshr.u64 $d1,$e,#@Sigma1[1] @ from NEON_00_15
|
||||
veor $s1,$t1 @ sigma0(X[i+1])
|
||||
vshr.u64 $d2,$e,#@Sigma1[2] @ from NEON_00_15
|
||||
vadd.i64 @X[$i%8],$s1
|
||||
___
|
||||
&NEON_00_15(2*$i,@_);
|
||||
}
|
||||
|
||||
$code.=<<___;
|
||||
#if __ARM_MAX_ARCH__>=7
|
||||
.arch armv7-a
|
||||
.fpu neon
|
||||
|
||||
.global sha512_block_data_order_neon
|
||||
.type sha512_block_data_order_neon,%function
|
||||
.align 4
|
||||
sha512_block_data_order_neon:
|
||||
.LNEON:
|
||||
dmb @ errata #451034 on early Cortex A8
|
||||
add $len,$inp,$len,lsl#7 @ len to point at the end of inp
|
||||
VFP_ABI_PUSH
|
||||
adrl $Ktbl,K512
|
||||
vldmia $ctx,{$A-$H} @ load context
|
||||
.Loop_neon:
|
||||
___
|
||||
for($i=0;$i<16;$i++) { &NEON_00_15($i,@V); unshift(@V,pop(@V)); }
|
||||
$code.=<<___;
|
||||
mov $cnt,#4
|
||||
.L16_79_neon:
|
||||
subs $cnt,#1
|
||||
___
|
||||
for(;$i<32;$i++) { &NEON_16_79($i,@V); unshift(@V,pop(@V)); }
|
||||
$code.=<<___;
|
||||
bne .L16_79_neon
|
||||
|
||||
vadd.i64 $A,d30 @ h+=Maj from the past
|
||||
vldmia $ctx,{d24-d31} @ load context to temp
|
||||
vadd.i64 q8,q12 @ vectorized accumulate
|
||||
vadd.i64 q9,q13
|
||||
vadd.i64 q10,q14
|
||||
vadd.i64 q11,q15
|
||||
vstmia $ctx,{$A-$H} @ save context
|
||||
teq $inp,$len
|
||||
sub $Ktbl,#640 @ rewind K512
|
||||
bne .Loop_neon
|
||||
|
||||
VFP_ABI_POP
|
||||
ret @ bx lr
|
||||
.size sha512_block_data_order_neon,.-sha512_block_data_order_neon
|
||||
#endif
|
||||
___
|
||||
}
|
||||
$code.=<<___;
|
||||
.asciz "SHA512 block transform for ARMv4/NEON, CRYPTOGAMS by <appro\@openssl.org>"
|
||||
.align 2
|
||||
#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
|
||||
.comm OPENSSL_armcap_P,4,4
|
||||
#endif
|
||||
___
|
||||
|
||||
$code =~ s/\`([^\`]*)\`/eval $1/gem;
|
||||
$code =~ s/\bbx\s+lr\b/.word\t0xe12fff1e/gm; # make it possible to compile with -march=armv4
|
||||
$code =~ s/\bret\b/bx lr/gm;
|
||||
|
||||
open SELF,$0;
|
||||
while(<SELF>) {
|
||||
next if (/^#!/);
|
||||
last if (!s/^#/@/ and !/^$/);
|
||||
print;
|
||||
}
|
||||
close SELF;
|
||||
|
||||
print $code;
|
||||
close STDOUT; # enforce flush
|
@ -1,455 +0,0 @@
|
||||
/* sha512-armv7-neon.S - ARM/NEON assembly implementation of SHA-512 transform
|
||||
*
|
||||
* Copyright © 2013-2014 Jussi Kivilinna <jussi.kivilinna@iki.fi>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
|
||||
|
||||
.syntax unified
|
||||
.code 32
|
||||
.fpu neon
|
||||
|
||||
.text
|
||||
|
||||
/* structure of SHA512_CONTEXT */
|
||||
#define hd_a 0
|
||||
#define hd_b ((hd_a) + 8)
|
||||
#define hd_c ((hd_b) + 8)
|
||||
#define hd_d ((hd_c) + 8)
|
||||
#define hd_e ((hd_d) + 8)
|
||||
#define hd_f ((hd_e) + 8)
|
||||
#define hd_g ((hd_f) + 8)
|
||||
|
||||
/* register macros */
|
||||
#define RK %r2
|
||||
|
||||
#define RA d0
|
||||
#define RB d1
|
||||
#define RC d2
|
||||
#define RD d3
|
||||
#define RE d4
|
||||
#define RF d5
|
||||
#define RG d6
|
||||
#define RH d7
|
||||
|
||||
#define RT0 d8
|
||||
#define RT1 d9
|
||||
#define RT2 d10
|
||||
#define RT3 d11
|
||||
#define RT4 d12
|
||||
#define RT5 d13
|
||||
#define RT6 d14
|
||||
#define RT7 d15
|
||||
|
||||
#define RT01q q4
|
||||
#define RT23q q5
|
||||
#define RT45q q6
|
||||
#define RT67q q7
|
||||
|
||||
#define RW0 d16
|
||||
#define RW1 d17
|
||||
#define RW2 d18
|
||||
#define RW3 d19
|
||||
#define RW4 d20
|
||||
#define RW5 d21
|
||||
#define RW6 d22
|
||||
#define RW7 d23
|
||||
#define RW8 d24
|
||||
#define RW9 d25
|
||||
#define RW10 d26
|
||||
#define RW11 d27
|
||||
#define RW12 d28
|
||||
#define RW13 d29
|
||||
#define RW14 d30
|
||||
#define RW15 d31
|
||||
|
||||
#define RW01q q8
|
||||
#define RW23q q9
|
||||
#define RW45q q10
|
||||
#define RW67q q11
|
||||
#define RW89q q12
|
||||
#define RW1011q q13
|
||||
#define RW1213q q14
|
||||
#define RW1415q q15
|
||||
|
||||
/***********************************************************************
|
||||
* ARM assembly implementation of sha512 transform
|
||||
***********************************************************************/
|
||||
#define rounds2_0_63(ra, rb, rc, rd, re, rf, rg, rh, rw0, rw1, rw01q, rw2, \
|
||||
rw23q, rw1415q, rw9, rw10, interleave_op, arg1) \
|
||||
/* t1 = h + Sum1 (e) + Ch (e, f, g) + k[t] + w[t]; */ \
|
||||
vshr.u64 RT2, re, #14; \
|
||||
vshl.u64 RT3, re, #64 - 14; \
|
||||
interleave_op(arg1); \
|
||||
vshr.u64 RT4, re, #18; \
|
||||
vshl.u64 RT5, re, #64 - 18; \
|
||||
vld1.64 {RT0}, [RK]!; \
|
||||
veor.64 RT23q, RT23q, RT45q; \
|
||||
vshr.u64 RT4, re, #41; \
|
||||
vshl.u64 RT5, re, #64 - 41; \
|
||||
vadd.u64 RT0, RT0, rw0; \
|
||||
veor.64 RT23q, RT23q, RT45q; \
|
||||
vmov.64 RT7, re; \
|
||||
veor.64 RT1, RT2, RT3; \
|
||||
vbsl.64 RT7, rf, rg; \
|
||||
\
|
||||
vadd.u64 RT1, RT1, rh; \
|
||||
vshr.u64 RT2, ra, #28; \
|
||||
vshl.u64 RT3, ra, #64 - 28; \
|
||||
vadd.u64 RT1, RT1, RT0; \
|
||||
vshr.u64 RT4, ra, #34; \
|
||||
vshl.u64 RT5, ra, #64 - 34; \
|
||||
vadd.u64 RT1, RT1, RT7; \
|
||||
\
|
||||
/* h = Sum0 (a) + Maj (a, b, c); */ \
|
||||
veor.64 RT23q, RT23q, RT45q; \
|
||||
vshr.u64 RT4, ra, #39; \
|
||||
vshl.u64 RT5, ra, #64 - 39; \
|
||||
veor.64 RT0, ra, rb; \
|
||||
veor.64 RT23q, RT23q, RT45q; \
|
||||
vbsl.64 RT0, rc, rb; \
|
||||
vadd.u64 rd, rd, RT1; /* d+=t1; */ \
|
||||
veor.64 rh, RT2, RT3; \
|
||||
\
|
||||
/* t1 = g + Sum1 (d) + Ch (d, e, f) + k[t] + w[t]; */ \
|
||||
vshr.u64 RT2, rd, #14; \
|
||||
vshl.u64 RT3, rd, #64 - 14; \
|
||||
vadd.u64 rh, rh, RT0; \
|
||||
vshr.u64 RT4, rd, #18; \
|
||||
vshl.u64 RT5, rd, #64 - 18; \
|
||||
vadd.u64 rh, rh, RT1; /* h+=t1; */ \
|
||||
vld1.64 {RT0}, [RK]!; \
|
||||
veor.64 RT23q, RT23q, RT45q; \
|
||||
vshr.u64 RT4, rd, #41; \
|
||||
vshl.u64 RT5, rd, #64 - 41; \
|
||||
vadd.u64 RT0, RT0, rw1; \
|
||||
veor.64 RT23q, RT23q, RT45q; \
|
||||
vmov.64 RT7, rd; \
|
||||
veor.64 RT1, RT2, RT3; \
|
||||
vbsl.64 RT7, re, rf; \
|
||||
\
|
||||
vadd.u64 RT1, RT1, rg; \
|
||||
vshr.u64 RT2, rh, #28; \
|
||||
vshl.u64 RT3, rh, #64 - 28; \
|
||||
vadd.u64 RT1, RT1, RT0; \
|
||||
vshr.u64 RT4, rh, #34; \
|
||||
vshl.u64 RT5, rh, #64 - 34; \
|
||||
vadd.u64 RT1, RT1, RT7; \
|
||||
\
|
||||
/* g = Sum0 (h) + Maj (h, a, b); */ \
|
||||
veor.64 RT23q, RT23q, RT45q; \
|
||||
vshr.u64 RT4, rh, #39; \
|
||||
vshl.u64 RT5, rh, #64 - 39; \
|
||||
veor.64 RT0, rh, ra; \
|
||||
veor.64 RT23q, RT23q, RT45q; \
|
||||
vbsl.64 RT0, rb, ra; \
|
||||
vadd.u64 rc, rc, RT1; /* c+=t1; */ \
|
||||
veor.64 rg, RT2, RT3; \
|
||||
\
|
||||
/* w[0] += S1 (w[14]) + w[9] + S0 (w[1]); */ \
|
||||
/* w[1] += S1 (w[15]) + w[10] + S0 (w[2]); */ \
|
||||
\
|
||||
/**** S0(w[1:2]) */ \
|
||||
\
|
||||
/* w[0:1] += w[9:10] */ \
|
||||
/* RT23q = rw1:rw2 */ \
|
||||
vext.u64 RT23q, rw01q, rw23q, #1; \
|
||||
vadd.u64 rw0, rw9; \
|
||||
vadd.u64 rg, rg, RT0; \
|
||||
vadd.u64 rw1, rw10;\
|
||||
vadd.u64 rg, rg, RT1; /* g+=t1; */ \
|
||||
\
|
||||
vshr.u64 RT45q, RT23q, #1; \
|
||||
vshl.u64 RT67q, RT23q, #64 - 1; \
|
||||
vshr.u64 RT01q, RT23q, #8; \
|
||||
veor.u64 RT45q, RT45q, RT67q; \
|
||||
vshl.u64 RT67q, RT23q, #64 - 8; \
|
||||
veor.u64 RT45q, RT45q, RT01q; \
|
||||
vshr.u64 RT01q, RT23q, #7; \
|
||||
veor.u64 RT45q, RT45q, RT67q; \
|
||||
\
|
||||
/**** S1(w[14:15]) */ \
|
||||
vshr.u64 RT23q, rw1415q, #6; \
|
||||
veor.u64 RT01q, RT01q, RT45q; \
|
||||
vshr.u64 RT45q, rw1415q, #19; \
|
||||
vshl.u64 RT67q, rw1415q, #64 - 19; \
|
||||
veor.u64 RT23q, RT23q, RT45q; \
|
||||
vshr.u64 RT45q, rw1415q, #61; \
|
||||
veor.u64 RT23q, RT23q, RT67q; \
|
||||
vshl.u64 RT67q, rw1415q, #64 - 61; \
|
||||
veor.u64 RT23q, RT23q, RT45q; \
|
||||
vadd.u64 rw01q, RT01q; /* w[0:1] += S(w[1:2]) */ \
|
||||
veor.u64 RT01q, RT23q, RT67q;
|
||||
#define vadd_RT01q(rw01q) \
|
||||
/* w[0:1] += S(w[14:15]) */ \
|
||||
vadd.u64 rw01q, RT01q;
|
||||
|
||||
#define dummy(_) /*_*/
|
||||
|
||||
#define rounds2_64_79(ra, rb, rc, rd, re, rf, rg, rh, rw0, rw1, \
|
||||
interleave_op1, arg1, interleave_op2, arg2) \
|
||||
/* t1 = h + Sum1 (e) + Ch (e, f, g) + k[t] + w[t]; */ \
|
||||
vshr.u64 RT2, re, #14; \
|
||||
vshl.u64 RT3, re, #64 - 14; \
|
||||
interleave_op1(arg1); \
|
||||
vshr.u64 RT4, re, #18; \
|
||||
vshl.u64 RT5, re, #64 - 18; \
|
||||
interleave_op2(arg2); \
|
||||
vld1.64 {RT0}, [RK]!; \
|
||||
veor.64 RT23q, RT23q, RT45q; \
|
||||
vshr.u64 RT4, re, #41; \
|
||||
vshl.u64 RT5, re, #64 - 41; \
|
||||
vadd.u64 RT0, RT0, rw0; \
|
||||
veor.64 RT23q, RT23q, RT45q; \
|
||||
vmov.64 RT7, re; \
|
||||
veor.64 RT1, RT2, RT3; \
|
||||
vbsl.64 RT7, rf, rg; \
|
||||
\
|
||||
vadd.u64 RT1, RT1, rh; \
|
||||
vshr.u64 RT2, ra, #28; \
|
||||
vshl.u64 RT3, ra, #64 - 28; \
|
||||
vadd.u64 RT1, RT1, RT0; \
|
||||
vshr.u64 RT4, ra, #34; \
|
||||
vshl.u64 RT5, ra, #64 - 34; \
|
||||
vadd.u64 RT1, RT1, RT7; \
|
||||
\
|
||||
/* h = Sum0 (a) + Maj (a, b, c); */ \
|
||||
veor.64 RT23q, RT23q, RT45q; \
|
||||
vshr.u64 RT4, ra, #39; \
|
||||
vshl.u64 RT5, ra, #64 - 39; \
|
||||
veor.64 RT0, ra, rb; \
|
||||
veor.64 RT23q, RT23q, RT45q; \
|
||||
vbsl.64 RT0, rc, rb; \
|
||||
vadd.u64 rd, rd, RT1; /* d+=t1; */ \
|
||||
veor.64 rh, RT2, RT3; \
|
||||
\
|
||||
/* t1 = g + Sum1 (d) + Ch (d, e, f) + k[t] + w[t]; */ \
|
||||
vshr.u64 RT2, rd, #14; \
|
||||
vshl.u64 RT3, rd, #64 - 14; \
|
||||
vadd.u64 rh, rh, RT0; \
|
||||
vshr.u64 RT4, rd, #18; \
|
||||
vshl.u64 RT5, rd, #64 - 18; \
|
||||
vadd.u64 rh, rh, RT1; /* h+=t1; */ \
|
||||
vld1.64 {RT0}, [RK]!; \
|
||||
veor.64 RT23q, RT23q, RT45q; \
|
||||
vshr.u64 RT4, rd, #41; \
|
||||
vshl.u64 RT5, rd, #64 - 41; \
|
||||
vadd.u64 RT0, RT0, rw1; \
|
||||
veor.64 RT23q, RT23q, RT45q; \
|
||||
vmov.64 RT7, rd; \
|
||||
veor.64 RT1, RT2, RT3; \
|
||||
vbsl.64 RT7, re, rf; \
|
||||
\
|
||||
vadd.u64 RT1, RT1, rg; \
|
||||
vshr.u64 RT2, rh, #28; \
|
||||
vshl.u64 RT3, rh, #64 - 28; \
|
||||
vadd.u64 RT1, RT1, RT0; \
|
||||
vshr.u64 RT4, rh, #34; \
|
||||
vshl.u64 RT5, rh, #64 - 34; \
|
||||
vadd.u64 RT1, RT1, RT7; \
|
||||
\
|
||||
/* g = Sum0 (h) + Maj (h, a, b); */ \
|
||||
veor.64 RT23q, RT23q, RT45q; \
|
||||
vshr.u64 RT4, rh, #39; \
|
||||
vshl.u64 RT5, rh, #64 - 39; \
|
||||
veor.64 RT0, rh, ra; \
|
||||
veor.64 RT23q, RT23q, RT45q; \
|
||||
vbsl.64 RT0, rb, ra; \
|
||||
vadd.u64 rc, rc, RT1; /* c+=t1; */ \
|
||||
veor.64 rg, RT2, RT3;
|
||||
#define vadd_rg_RT0(rg) \
|
||||
vadd.u64 rg, rg, RT0;
|
||||
#define vadd_rg_RT1(rg) \
|
||||
vadd.u64 rg, rg, RT1; /* g+=t1; */
|
||||
|
||||
.align 3
|
||||
ENTRY(sha512_transform_neon)
|
||||
/* Input:
|
||||
* %r0: SHA512_CONTEXT
|
||||
* %r1: data
|
||||
* %r2: u64 k[] constants
|
||||
* %r3: nblks
|
||||
*/
|
||||
push {%lr};
|
||||
|
||||
mov %lr, #0;
|
||||
|
||||
/* Load context to d0-d7 */
|
||||
vld1.64 {RA-RD}, [%r0]!;
|
||||
vld1.64 {RE-RH}, [%r0];
|
||||
sub %r0, #(4*8);
|
||||
|
||||
/* Load input to w[16], d16-d31 */
|
||||
/* NOTE: Assumes that on ARMv7 unaligned accesses are always allowed. */
|
||||
vld1.64 {RW0-RW3}, [%r1]!;
|
||||
vld1.64 {RW4-RW7}, [%r1]!;
|
||||
vld1.64 {RW8-RW11}, [%r1]!;
|
||||
vld1.64 {RW12-RW15}, [%r1]!;
|
||||
#ifdef __ARMEL__
|
||||
/* byteswap */
|
||||
vrev64.8 RW01q, RW01q;
|
||||
vrev64.8 RW23q, RW23q;
|
||||
vrev64.8 RW45q, RW45q;
|
||||
vrev64.8 RW67q, RW67q;
|
||||
vrev64.8 RW89q, RW89q;
|
||||
vrev64.8 RW1011q, RW1011q;
|
||||
vrev64.8 RW1213q, RW1213q;
|
||||
vrev64.8 RW1415q, RW1415q;
|
||||
#endif
|
||||
|
||||
/* EABI says that d8-d15 must be preserved by callee. */
|
||||
/*vpush {RT0-RT7};*/
|
||||
|
||||
.Loop:
|
||||
rounds2_0_63(RA, RB, RC, RD, RE, RF, RG, RH, RW0, RW1, RW01q, RW2,
|
||||
RW23q, RW1415q, RW9, RW10, dummy, _);
|
||||
b .Lenter_rounds;
|
||||
|
||||
.Loop_rounds:
|
||||
rounds2_0_63(RA, RB, RC, RD, RE, RF, RG, RH, RW0, RW1, RW01q, RW2,
|
||||
RW23q, RW1415q, RW9, RW10, vadd_RT01q, RW1415q);
|
||||
.Lenter_rounds:
|
||||
rounds2_0_63(RG, RH, RA, RB, RC, RD, RE, RF, RW2, RW3, RW23q, RW4,
|
||||
RW45q, RW01q, RW11, RW12, vadd_RT01q, RW01q);
|
||||
rounds2_0_63(RE, RF, RG, RH, RA, RB, RC, RD, RW4, RW5, RW45q, RW6,
|
||||
RW67q, RW23q, RW13, RW14, vadd_RT01q, RW23q);
|
||||
rounds2_0_63(RC, RD, RE, RF, RG, RH, RA, RB, RW6, RW7, RW67q, RW8,
|
||||
RW89q, RW45q, RW15, RW0, vadd_RT01q, RW45q);
|
||||
rounds2_0_63(RA, RB, RC, RD, RE, RF, RG, RH, RW8, RW9, RW89q, RW10,
|
||||
RW1011q, RW67q, RW1, RW2, vadd_RT01q, RW67q);
|
||||
rounds2_0_63(RG, RH, RA, RB, RC, RD, RE, RF, RW10, RW11, RW1011q, RW12,
|
||||
RW1213q, RW89q, RW3, RW4, vadd_RT01q, RW89q);
|
||||
add %lr, #16;
|
||||
rounds2_0_63(RE, RF, RG, RH, RA, RB, RC, RD, RW12, RW13, RW1213q, RW14,
|
||||
RW1415q, RW1011q, RW5, RW6, vadd_RT01q, RW1011q);
|
||||
cmp %lr, #64;
|
||||
rounds2_0_63(RC, RD, RE, RF, RG, RH, RA, RB, RW14, RW15, RW1415q, RW0,
|
||||
RW01q, RW1213q, RW7, RW8, vadd_RT01q, RW1213q);
|
||||
bne .Loop_rounds;
|
||||
|
||||
subs %r3, #1;
|
||||
|
||||
rounds2_64_79(RA, RB, RC, RD, RE, RF, RG, RH, RW0, RW1,
|
||||
vadd_RT01q, RW1415q, dummy, _);
|
||||
rounds2_64_79(RG, RH, RA, RB, RC, RD, RE, RF, RW2, RW3,
|
||||
vadd_rg_RT0, RG, vadd_rg_RT1, RG);
|
||||
beq .Lhandle_tail;
|
||||
vld1.64 {RW0-RW3}, [%r1]!;
|
||||
rounds2_64_79(RE, RF, RG, RH, RA, RB, RC, RD, RW4, RW5,
|
||||
vadd_rg_RT0, RE, vadd_rg_RT1, RE);
|
||||
rounds2_64_79(RC, RD, RE, RF, RG, RH, RA, RB, RW6, RW7,
|
||||
vadd_rg_RT0, RC, vadd_rg_RT1, RC);
|
||||
#ifdef __ARMEL__
|
||||
vrev64.8 RW01q, RW01q;
|
||||
vrev64.8 RW23q, RW23q;
|
||||
#endif
|
||||
vld1.64 {RW4-RW7}, [%r1]!;
|
||||
rounds2_64_79(RA, RB, RC, RD, RE, RF, RG, RH, RW8, RW9,
|
||||
vadd_rg_RT0, RA, vadd_rg_RT1, RA);
|
||||
rounds2_64_79(RG, RH, RA, RB, RC, RD, RE, RF, RW10, RW11,
|
||||
vadd_rg_RT0, RG, vadd_rg_RT1, RG);
|
||||
#ifdef __ARMEL__
|
||||
vrev64.8 RW45q, RW45q;
|
||||
vrev64.8 RW67q, RW67q;
|
||||
#endif
|
||||
vld1.64 {RW8-RW11}, [%r1]!;
|
||||
rounds2_64_79(RE, RF, RG, RH, RA, RB, RC, RD, RW12, RW13,
|
||||
vadd_rg_RT0, RE, vadd_rg_RT1, RE);
|
||||
rounds2_64_79(RC, RD, RE, RF, RG, RH, RA, RB, RW14, RW15,
|
||||
vadd_rg_RT0, RC, vadd_rg_RT1, RC);
|
||||
#ifdef __ARMEL__
|
||||
vrev64.8 RW89q, RW89q;
|
||||
vrev64.8 RW1011q, RW1011q;
|
||||
#endif
|
||||
vld1.64 {RW12-RW15}, [%r1]!;
|
||||
vadd_rg_RT0(RA);
|
||||
vadd_rg_RT1(RA);
|
||||
|
||||
/* Load context */
|
||||
vld1.64 {RT0-RT3}, [%r0]!;
|
||||
vld1.64 {RT4-RT7}, [%r0];
|
||||
sub %r0, #(4*8);
|
||||
|
||||
#ifdef __ARMEL__
|
||||
vrev64.8 RW1213q, RW1213q;
|
||||
vrev64.8 RW1415q, RW1415q;
|
||||
#endif
|
||||
|
||||
vadd.u64 RA, RT0;
|
||||
vadd.u64 RB, RT1;
|
||||
vadd.u64 RC, RT2;
|
||||
vadd.u64 RD, RT3;
|
||||
vadd.u64 RE, RT4;
|
||||
vadd.u64 RF, RT5;
|
||||
vadd.u64 RG, RT6;
|
||||
vadd.u64 RH, RT7;
|
||||
|
||||
/* Store the first half of context */
|
||||
vst1.64 {RA-RD}, [%r0]!;
|
||||
sub RK, $(8*80);
|
||||
vst1.64 {RE-RH}, [%r0]; /* Store the last half of context */
|
||||
mov %lr, #0;
|
||||
sub %r0, #(4*8);
|
||||
|
||||
b .Loop;
|
||||
|
||||
.Lhandle_tail:
|
||||
rounds2_64_79(RE, RF, RG, RH, RA, RB, RC, RD, RW4, RW5,
|
||||
vadd_rg_RT0, RE, vadd_rg_RT1, RE);
|
||||
rounds2_64_79(RC, RD, RE, RF, RG, RH, RA, RB, RW6, RW7,
|
||||
vadd_rg_RT0, RC, vadd_rg_RT1, RC);
|
||||
rounds2_64_79(RA, RB, RC, RD, RE, RF, RG, RH, RW8, RW9,
|
||||
vadd_rg_RT0, RA, vadd_rg_RT1, RA);
|
||||
rounds2_64_79(RG, RH, RA, RB, RC, RD, RE, RF, RW10, RW11,
|
||||
vadd_rg_RT0, RG, vadd_rg_RT1, RG);
|
||||
rounds2_64_79(RE, RF, RG, RH, RA, RB, RC, RD, RW12, RW13,
|
||||
vadd_rg_RT0, RE, vadd_rg_RT1, RE);
|
||||
rounds2_64_79(RC, RD, RE, RF, RG, RH, RA, RB, RW14, RW15,
|
||||
vadd_rg_RT0, RC, vadd_rg_RT1, RC);
|
||||
|
||||
/* Load context to d16-d23 */
|
||||
vld1.64 {RW0-RW3}, [%r0]!;
|
||||
vadd_rg_RT0(RA);
|
||||
vld1.64 {RW4-RW7}, [%r0];
|
||||
vadd_rg_RT1(RA);
|
||||
sub %r0, #(4*8);
|
||||
|
||||
vadd.u64 RA, RW0;
|
||||
vadd.u64 RB, RW1;
|
||||
vadd.u64 RC, RW2;
|
||||
vadd.u64 RD, RW3;
|
||||
vadd.u64 RE, RW4;
|
||||
vadd.u64 RF, RW5;
|
||||
vadd.u64 RG, RW6;
|
||||
vadd.u64 RH, RW7;
|
||||
|
||||
/* Store the first half of context */
|
||||
vst1.64 {RA-RD}, [%r0]!;
|
||||
|
||||
/* Clear used registers */
|
||||
/* d16-d31 */
|
||||
veor.u64 RW01q, RW01q;
|
||||
veor.u64 RW23q, RW23q;
|
||||
veor.u64 RW45q, RW45q;
|
||||
veor.u64 RW67q, RW67q;
|
||||
vst1.64 {RE-RH}, [%r0]; /* Store the last half of context */
|
||||
veor.u64 RW89q, RW89q;
|
||||
veor.u64 RW1011q, RW1011q;
|
||||
veor.u64 RW1213q, RW1213q;
|
||||
veor.u64 RW1415q, RW1415q;
|
||||
/* d8-d15 */
|
||||
/*vpop {RT0-RT7};*/
|
||||
/* d0-d7 (q0-q3) */
|
||||
veor.u64 %q0, %q0;
|
||||
veor.u64 %q1, %q1;
|
||||
veor.u64 %q2, %q2;
|
||||
veor.u64 %q3, %q3;
|
||||
|
||||
pop {%pc};
|
||||
ENDPROC(sha512_transform_neon)
|
1861
arch/arm/crypto/sha512-core.S_shipped
Normal file
1861
arch/arm/crypto/sha512-core.S_shipped
Normal file
File diff suppressed because it is too large
Load Diff
121
arch/arm/crypto/sha512-glue.c
Normal file
121
arch/arm/crypto/sha512-glue.c
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* sha512-glue.c - accelerated SHA-384/512 for ARM
|
||||
*
|
||||
* Copyright (C) 2015 Linaro Ltd <ard.biesheuvel@linaro.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <crypto/internal/hash.h>
|
||||
#include <crypto/sha.h>
|
||||
#include <crypto/sha512_base.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/hwcap.h>
|
||||
#include <asm/neon.h>
|
||||
|
||||
#include "sha512.h"
|
||||
|
||||
MODULE_DESCRIPTION("Accelerated SHA-384/SHA-512 secure hash for ARM");
|
||||
MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
MODULE_ALIAS_CRYPTO("sha384");
|
||||
MODULE_ALIAS_CRYPTO("sha512");
|
||||
MODULE_ALIAS_CRYPTO("sha384-arm");
|
||||
MODULE_ALIAS_CRYPTO("sha512-arm");
|
||||
|
||||
asmlinkage void sha512_block_data_order(u64 *state, u8 const *src, int blocks);
|
||||
|
||||
int sha512_arm_update(struct shash_desc *desc, const u8 *data,
|
||||
unsigned int len)
|
||||
{
|
||||
return sha512_base_do_update(desc, data, len,
|
||||
(sha512_block_fn *)sha512_block_data_order);
|
||||
}
|
||||
|
||||
int sha512_arm_final(struct shash_desc *desc, u8 *out)
|
||||
{
|
||||
sha512_base_do_finalize(desc,
|
||||
(sha512_block_fn *)sha512_block_data_order);
|
||||
return sha512_base_finish(desc, out);
|
||||
}
|
||||
|
||||
int sha512_arm_finup(struct shash_desc *desc, const u8 *data,
|
||||
unsigned int len, u8 *out)
|
||||
{
|
||||
sha512_base_do_update(desc, data, len,
|
||||
(sha512_block_fn *)sha512_block_data_order);
|
||||
return sha512_arm_final(desc, out);
|
||||
}
|
||||
|
||||
static struct shash_alg sha512_arm_algs[] = { {
|
||||
.init = sha384_base_init,
|
||||
.update = sha512_arm_update,
|
||||
.final = sha512_arm_final,
|
||||
.finup = sha512_arm_finup,
|
||||
.descsize = sizeof(struct sha512_state),
|
||||
.digestsize = SHA384_DIGEST_SIZE,
|
||||
.base = {
|
||||
.cra_name = "sha384",
|
||||
.cra_driver_name = "sha384-arm",
|
||||
.cra_priority = 250,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_SHASH,
|
||||
.cra_blocksize = SHA512_BLOCK_SIZE,
|
||||
.cra_module = THIS_MODULE,
|
||||
}
|
||||
}, {
|
||||
.init = sha512_base_init,
|
||||
.update = sha512_arm_update,
|
||||
.final = sha512_arm_final,
|
||||
.finup = sha512_arm_finup,
|
||||
.descsize = sizeof(struct sha512_state),
|
||||
.digestsize = SHA512_DIGEST_SIZE,
|
||||
.base = {
|
||||
.cra_name = "sha512",
|
||||
.cra_driver_name = "sha512-arm",
|
||||
.cra_priority = 250,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_SHASH,
|
||||
.cra_blocksize = SHA512_BLOCK_SIZE,
|
||||
.cra_module = THIS_MODULE,
|
||||
}
|
||||
} };
|
||||
|
||||
static int __init sha512_arm_mod_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = crypto_register_shashes(sha512_arm_algs,
|
||||
ARRAY_SIZE(sha512_arm_algs));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (IS_ENABLED(CONFIG_KERNEL_MODE_NEON) && cpu_has_neon()) {
|
||||
err = crypto_register_shashes(sha512_neon_algs,
|
||||
ARRAY_SIZE(sha512_neon_algs));
|
||||
if (err)
|
||||
goto err_unregister;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_unregister:
|
||||
crypto_unregister_shashes(sha512_arm_algs,
|
||||
ARRAY_SIZE(sha512_arm_algs));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit sha512_arm_mod_fini(void)
|
||||
{
|
||||
crypto_unregister_shashes(sha512_arm_algs,
|
||||
ARRAY_SIZE(sha512_arm_algs));
|
||||
if (IS_ENABLED(CONFIG_KERNEL_MODE_NEON) && cpu_has_neon())
|
||||
crypto_unregister_shashes(sha512_neon_algs,
|
||||
ARRAY_SIZE(sha512_neon_algs));
|
||||
}
|
||||
|
||||
module_init(sha512_arm_mod_init);
|
||||
module_exit(sha512_arm_mod_fini);
|
98
arch/arm/crypto/sha512-neon-glue.c
Normal file
98
arch/arm/crypto/sha512-neon-glue.c
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* sha512-neon-glue.c - accelerated SHA-384/512 for ARM NEON
|
||||
*
|
||||
* Copyright (C) 2015 Linaro Ltd <ard.biesheuvel@linaro.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <crypto/internal/hash.h>
|
||||
#include <crypto/sha.h>
|
||||
#include <crypto/sha512_base.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/simd.h>
|
||||
#include <asm/neon.h>
|
||||
|
||||
#include "sha512.h"
|
||||
|
||||
MODULE_ALIAS_CRYPTO("sha384-neon");
|
||||
MODULE_ALIAS_CRYPTO("sha512-neon");
|
||||
|
||||
asmlinkage void sha512_block_data_order_neon(u64 *state, u8 const *src,
|
||||
int blocks);
|
||||
|
||||
static int sha512_neon_update(struct shash_desc *desc, const u8 *data,
|
||||
unsigned int len)
|
||||
{
|
||||
struct sha512_state *sctx = shash_desc_ctx(desc);
|
||||
|
||||
if (!may_use_simd() ||
|
||||
(sctx->count[0] % SHA512_BLOCK_SIZE) + len < SHA512_BLOCK_SIZE)
|
||||
return sha512_arm_update(desc, data, len);
|
||||
|
||||
kernel_neon_begin();
|
||||
sha512_base_do_update(desc, data, len,
|
||||
(sha512_block_fn *)sha512_block_data_order_neon);
|
||||
kernel_neon_end();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sha512_neon_finup(struct shash_desc *desc, const u8 *data,
|
||||
unsigned int len, u8 *out)
|
||||
{
|
||||
if (!may_use_simd())
|
||||
return sha512_arm_finup(desc, data, len, out);
|
||||
|
||||
kernel_neon_begin();
|
||||
if (len)
|
||||
sha512_base_do_update(desc, data, len,
|
||||
(sha512_block_fn *)sha512_block_data_order_neon);
|
||||
sha512_base_do_finalize(desc,
|
||||
(sha512_block_fn *)sha512_block_data_order_neon);
|
||||
kernel_neon_end();
|
||||
|
||||
return sha512_base_finish(desc, out);
|
||||
}
|
||||
|
||||
static int sha512_neon_final(struct shash_desc *desc, u8 *out)
|
||||
{
|
||||
return sha512_neon_finup(desc, NULL, 0, out);
|
||||
}
|
||||
|
||||
struct shash_alg sha512_neon_algs[] = { {
|
||||
.init = sha384_base_init,
|
||||
.update = sha512_neon_update,
|
||||
.final = sha512_neon_final,
|
||||
.finup = sha512_neon_finup,
|
||||
.descsize = sizeof(struct sha512_state),
|
||||
.digestsize = SHA384_DIGEST_SIZE,
|
||||
.base = {
|
||||
.cra_name = "sha384",
|
||||
.cra_driver_name = "sha384-neon",
|
||||
.cra_priority = 300,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_SHASH,
|
||||
.cra_blocksize = SHA384_BLOCK_SIZE,
|
||||
.cra_module = THIS_MODULE,
|
||||
|
||||
}
|
||||
}, {
|
||||
.init = sha512_base_init,
|
||||
.update = sha512_neon_update,
|
||||
.final = sha512_neon_final,
|
||||
.finup = sha512_neon_finup,
|
||||
.descsize = sizeof(struct sha512_state),
|
||||
.digestsize = SHA512_DIGEST_SIZE,
|
||||
.base = {
|
||||
.cra_name = "sha512",
|
||||
.cra_driver_name = "sha512-neon",
|
||||
.cra_priority = 300,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_SHASH,
|
||||
.cra_blocksize = SHA512_BLOCK_SIZE,
|
||||
.cra_module = THIS_MODULE,
|
||||
}
|
||||
} };
|
8
arch/arm/crypto/sha512.h
Normal file
8
arch/arm/crypto/sha512.h
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
int sha512_arm_update(struct shash_desc *desc, const u8 *data,
|
||||
unsigned int len);
|
||||
|
||||
int sha512_arm_finup(struct shash_desc *desc, const u8 *data,
|
||||
unsigned int len, u8 *out);
|
||||
|
||||
extern struct shash_alg sha512_neon_algs[2];
|
@ -1,305 +0,0 @@
|
||||
/*
|
||||
* Glue code for the SHA512 Secure Hash Algorithm assembly implementation
|
||||
* using NEON instructions.
|
||||
*
|
||||
* Copyright © 2014 Jussi Kivilinna <jussi.kivilinna@iki.fi>
|
||||
*
|
||||
* This file is based on sha512_ssse3_glue.c:
|
||||
* Copyright (C) 2013 Intel Corporation
|
||||
* Author: Tim Chen <tim.c.chen@linux.intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <crypto/internal/hash.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/cryptohash.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/string.h>
|
||||
#include <crypto/sha.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/simd.h>
|
||||
#include <asm/neon.h>
|
||||
|
||||
|
||||
static const u64 sha512_k[] = {
|
||||
0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
|
||||
0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
|
||||
0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
|
||||
0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
|
||||
0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
|
||||
0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
|
||||
0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
|
||||
0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
|
||||
0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
|
||||
0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
|
||||
0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
|
||||
0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
|
||||
0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
|
||||
0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
|
||||
0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
|
||||
0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
|
||||
0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
|
||||
0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
|
||||
0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
|
||||
0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
|
||||
0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
|
||||
0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
|
||||
0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
|
||||
0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
|
||||
0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
|
||||
0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
|
||||
0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
|
||||
0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
|
||||
0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
|
||||
0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
|
||||
0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
|
||||
0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
|
||||
0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
|
||||
0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
|
||||
0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
|
||||
0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
|
||||
0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
|
||||
0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
|
||||
0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
|
||||
0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
|
||||
};
|
||||
|
||||
|
||||
asmlinkage void sha512_transform_neon(u64 *digest, const void *data,
|
||||
const u64 k[], unsigned int num_blks);
|
||||
|
||||
|
||||
static int sha512_neon_init(struct shash_desc *desc)
|
||||
{
|
||||
struct sha512_state *sctx = shash_desc_ctx(desc);
|
||||
|
||||
sctx->state[0] = SHA512_H0;
|
||||
sctx->state[1] = SHA512_H1;
|
||||
sctx->state[2] = SHA512_H2;
|
||||
sctx->state[3] = SHA512_H3;
|
||||
sctx->state[4] = SHA512_H4;
|
||||
sctx->state[5] = SHA512_H5;
|
||||
sctx->state[6] = SHA512_H6;
|
||||
sctx->state[7] = SHA512_H7;
|
||||
sctx->count[0] = sctx->count[1] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __sha512_neon_update(struct shash_desc *desc, const u8 *data,
|
||||
unsigned int len, unsigned int partial)
|
||||
{
|
||||
struct sha512_state *sctx = shash_desc_ctx(desc);
|
||||
unsigned int done = 0;
|
||||
|
||||
sctx->count[0] += len;
|
||||
if (sctx->count[0] < len)
|
||||
sctx->count[1]++;
|
||||
|
||||
if (partial) {
|
||||
done = SHA512_BLOCK_SIZE - partial;
|
||||
memcpy(sctx->buf + partial, data, done);
|
||||
sha512_transform_neon(sctx->state, sctx->buf, sha512_k, 1);
|
||||
}
|
||||
|
||||
if (len - done >= SHA512_BLOCK_SIZE) {
|
||||
const unsigned int rounds = (len - done) / SHA512_BLOCK_SIZE;
|
||||
|
||||
sha512_transform_neon(sctx->state, data + done, sha512_k,
|
||||
rounds);
|
||||
|
||||
done += rounds * SHA512_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
memcpy(sctx->buf, data + done, len - done);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sha512_neon_update(struct shash_desc *desc, const u8 *data,
|
||||
unsigned int len)
|
||||
{
|
||||
struct sha512_state *sctx = shash_desc_ctx(desc);
|
||||
unsigned int partial = sctx->count[0] % SHA512_BLOCK_SIZE;
|
||||
int res;
|
||||
|
||||
/* Handle the fast case right here */
|
||||
if (partial + len < SHA512_BLOCK_SIZE) {
|
||||
sctx->count[0] += len;
|
||||
if (sctx->count[0] < len)
|
||||
sctx->count[1]++;
|
||||
memcpy(sctx->buf + partial, data, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!may_use_simd()) {
|
||||
res = crypto_sha512_update(desc, data, len);
|
||||
} else {
|
||||
kernel_neon_begin();
|
||||
res = __sha512_neon_update(desc, data, len, partial);
|
||||
kernel_neon_end();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* Add padding and return the message digest. */
|
||||
static int sha512_neon_final(struct shash_desc *desc, u8 *out)
|
||||
{
|
||||
struct sha512_state *sctx = shash_desc_ctx(desc);
|
||||
unsigned int i, index, padlen;
|
||||
__be64 *dst = (__be64 *)out;
|
||||
__be64 bits[2];
|
||||
static const u8 padding[SHA512_BLOCK_SIZE] = { 0x80, };
|
||||
|
||||
/* save number of bits */
|
||||
bits[1] = cpu_to_be64(sctx->count[0] << 3);
|
||||
bits[0] = cpu_to_be64(sctx->count[1] << 3 | sctx->count[0] >> 61);
|
||||
|
||||
/* Pad out to 112 mod 128 and append length */
|
||||
index = sctx->count[0] & 0x7f;
|
||||
padlen = (index < 112) ? (112 - index) : ((128+112) - index);
|
||||
|
||||
if (!may_use_simd()) {
|
||||
crypto_sha512_update(desc, padding, padlen);
|
||||
crypto_sha512_update(desc, (const u8 *)&bits, sizeof(bits));
|
||||
} else {
|
||||
kernel_neon_begin();
|
||||
/* We need to fill a whole block for __sha512_neon_update() */
|
||||
if (padlen <= 112) {
|
||||
sctx->count[0] += padlen;
|
||||
if (sctx->count[0] < padlen)
|
||||
sctx->count[1]++;
|
||||
memcpy(sctx->buf + index, padding, padlen);
|
||||
} else {
|
||||
__sha512_neon_update(desc, padding, padlen, index);
|
||||
}
|
||||
__sha512_neon_update(desc, (const u8 *)&bits,
|
||||
sizeof(bits), 112);
|
||||
kernel_neon_end();
|
||||
}
|
||||
|
||||
/* Store state in digest */
|
||||
for (i = 0; i < 8; i++)
|
||||
dst[i] = cpu_to_be64(sctx->state[i]);
|
||||
|
||||
/* Wipe context */
|
||||
memset(sctx, 0, sizeof(*sctx));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sha512_neon_export(struct shash_desc *desc, void *out)
|
||||
{
|
||||
struct sha512_state *sctx = shash_desc_ctx(desc);
|
||||
|
||||
memcpy(out, sctx, sizeof(*sctx));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sha512_neon_import(struct shash_desc *desc, const void *in)
|
||||
{
|
||||
struct sha512_state *sctx = shash_desc_ctx(desc);
|
||||
|
||||
memcpy(sctx, in, sizeof(*sctx));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sha384_neon_init(struct shash_desc *desc)
|
||||
{
|
||||
struct sha512_state *sctx = shash_desc_ctx(desc);
|
||||
|
||||
sctx->state[0] = SHA384_H0;
|
||||
sctx->state[1] = SHA384_H1;
|
||||
sctx->state[2] = SHA384_H2;
|
||||
sctx->state[3] = SHA384_H3;
|
||||
sctx->state[4] = SHA384_H4;
|
||||
sctx->state[5] = SHA384_H5;
|
||||
sctx->state[6] = SHA384_H6;
|
||||
sctx->state[7] = SHA384_H7;
|
||||
|
||||
sctx->count[0] = sctx->count[1] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sha384_neon_final(struct shash_desc *desc, u8 *hash)
|
||||
{
|
||||
u8 D[SHA512_DIGEST_SIZE];
|
||||
|
||||
sha512_neon_final(desc, D);
|
||||
|
||||
memcpy(hash, D, SHA384_DIGEST_SIZE);
|
||||
memzero_explicit(D, SHA512_DIGEST_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct shash_alg algs[] = { {
|
||||
.digestsize = SHA512_DIGEST_SIZE,
|
||||
.init = sha512_neon_init,
|
||||
.update = sha512_neon_update,
|
||||
.final = sha512_neon_final,
|
||||
.export = sha512_neon_export,
|
||||
.import = sha512_neon_import,
|
||||
.descsize = sizeof(struct sha512_state),
|
||||
.statesize = sizeof(struct sha512_state),
|
||||
.base = {
|
||||
.cra_name = "sha512",
|
||||
.cra_driver_name = "sha512-neon",
|
||||
.cra_priority = 250,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_SHASH,
|
||||
.cra_blocksize = SHA512_BLOCK_SIZE,
|
||||
.cra_module = THIS_MODULE,
|
||||
}
|
||||
}, {
|
||||
.digestsize = SHA384_DIGEST_SIZE,
|
||||
.init = sha384_neon_init,
|
||||
.update = sha512_neon_update,
|
||||
.final = sha384_neon_final,
|
||||
.export = sha512_neon_export,
|
||||
.import = sha512_neon_import,
|
||||
.descsize = sizeof(struct sha512_state),
|
||||
.statesize = sizeof(struct sha512_state),
|
||||
.base = {
|
||||
.cra_name = "sha384",
|
||||
.cra_driver_name = "sha384-neon",
|
||||
.cra_priority = 250,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_SHASH,
|
||||
.cra_blocksize = SHA384_BLOCK_SIZE,
|
||||
.cra_module = THIS_MODULE,
|
||||
}
|
||||
} };
|
||||
|
||||
static int __init sha512_neon_mod_init(void)
|
||||
{
|
||||
if (!cpu_has_neon())
|
||||
return -ENODEV;
|
||||
|
||||
return crypto_register_shashes(algs, ARRAY_SIZE(algs));
|
||||
}
|
||||
|
||||
static void __exit sha512_neon_mod_fini(void)
|
||||
{
|
||||
crypto_unregister_shashes(algs, ARRAY_SIZE(algs));
|
||||
}
|
||||
|
||||
module_init(sha512_neon_mod_init);
|
||||
module_exit(sha512_neon_mod_fini);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("SHA512 Secure Hash Algorithm, NEON accelerated");
|
||||
|
||||
MODULE_ALIAS_CRYPTO("sha512");
|
||||
MODULE_ALIAS_CRYPTO("sha384");
|
@ -13,7 +13,7 @@
|
||||
#include <crypto/aes.h>
|
||||
#include <crypto/algapi.h>
|
||||
#include <crypto/scatterwalk.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <crypto/internal/aead.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "aes-ce-setkey.h"
|
||||
|
@ -69,10 +69,10 @@ static int octeon_md5_init(struct shash_desc *desc)
|
||||
{
|
||||
struct md5_state *mctx = shash_desc_ctx(desc);
|
||||
|
||||
mctx->hash[0] = cpu_to_le32(0x67452301);
|
||||
mctx->hash[1] = cpu_to_le32(0xefcdab89);
|
||||
mctx->hash[2] = cpu_to_le32(0x98badcfe);
|
||||
mctx->hash[3] = cpu_to_le32(0x10325476);
|
||||
mctx->hash[0] = cpu_to_le32(MD5_H0);
|
||||
mctx->hash[1] = cpu_to_le32(MD5_H1);
|
||||
mctx->hash[2] = cpu_to_le32(MD5_H2);
|
||||
mctx->hash[3] = cpu_to_le32(MD5_H3);
|
||||
mctx->byte_count = 0;
|
||||
|
||||
return 0;
|
||||
|
@ -8,6 +8,7 @@
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/clocksource.h>
|
||||
@ -106,6 +107,7 @@ cycles_t get_cycles(void)
|
||||
{
|
||||
return nios2_timer_read(&nios2_cs.cs);
|
||||
}
|
||||
EXPORT_SYMBOL(get_cycles);
|
||||
|
||||
static void nios2_timer_start(struct nios2_timer *timer)
|
||||
{
|
||||
|
@ -37,10 +37,10 @@ static int ppc_md5_init(struct shash_desc *desc)
|
||||
{
|
||||
struct md5_state *sctx = shash_desc_ctx(desc);
|
||||
|
||||
sctx->hash[0] = 0x67452301;
|
||||
sctx->hash[1] = 0xefcdab89;
|
||||
sctx->hash[2] = 0x98badcfe;
|
||||
sctx->hash[3] = 0x10325476;
|
||||
sctx->hash[0] = MD5_H0;
|
||||
sctx->hash[1] = MD5_H1;
|
||||
sctx->hash[2] = MD5_H2;
|
||||
sctx->hash[3] = MD5_H3;
|
||||
sctx->byte_count = 0;
|
||||
|
||||
return 0;
|
||||
|
184
arch/powerpc/include/asm/icswx.h
Normal file
184
arch/powerpc/include/asm/icswx.h
Normal file
@ -0,0 +1,184 @@
|
||||
/*
|
||||
* ICSWX api
|
||||
*
|
||||
* Copyright (C) 2015 IBM Corp.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This provides the Initiate Coprocessor Store Word Indexed (ICSWX)
|
||||
* instruction. This instruction is used to communicate with PowerPC
|
||||
* coprocessors. This also provides definitions of the structures used
|
||||
* to communicate with the coprocessor.
|
||||
*
|
||||
* The RFC02130: Coprocessor Architecture document is the reference for
|
||||
* everything in this file unless otherwise noted.
|
||||
*/
|
||||
#ifndef _ARCH_POWERPC_INCLUDE_ASM_ICSWX_H_
|
||||
#define _ARCH_POWERPC_INCLUDE_ASM_ICSWX_H_
|
||||
|
||||
#include <asm/ppc-opcode.h> /* for PPC_ICSWX */
|
||||
|
||||
/* Chapter 6.5.8 Coprocessor-Completion Block (CCB) */
|
||||
|
||||
#define CCB_VALUE (0x3fffffffffffffff)
|
||||
#define CCB_ADDRESS (0xfffffffffffffff8)
|
||||
#define CCB_CM (0x0000000000000007)
|
||||
#define CCB_CM0 (0x0000000000000004)
|
||||
#define CCB_CM12 (0x0000000000000003)
|
||||
|
||||
#define CCB_CM0_ALL_COMPLETIONS (0x0)
|
||||
#define CCB_CM0_LAST_IN_CHAIN (0x4)
|
||||
#define CCB_CM12_STORE (0x0)
|
||||
#define CCB_CM12_INTERRUPT (0x1)
|
||||
|
||||
#define CCB_SIZE (0x10)
|
||||
#define CCB_ALIGN CCB_SIZE
|
||||
|
||||
struct coprocessor_completion_block {
|
||||
__be64 value;
|
||||
__be64 address;
|
||||
} __packed __aligned(CCB_ALIGN);
|
||||
|
||||
|
||||
/* Chapter 6.5.7 Coprocessor-Status Block (CSB) */
|
||||
|
||||
#define CSB_V (0x80)
|
||||
#define CSB_F (0x04)
|
||||
#define CSB_CH (0x03)
|
||||
#define CSB_CE_INCOMPLETE (0x80)
|
||||
#define CSB_CE_TERMINATION (0x40)
|
||||
#define CSB_CE_TPBC (0x20)
|
||||
|
||||
#define CSB_CC_SUCCESS (0)
|
||||
#define CSB_CC_INVALID_ALIGN (1)
|
||||
#define CSB_CC_OPERAND_OVERLAP (2)
|
||||
#define CSB_CC_DATA_LENGTH (3)
|
||||
#define CSB_CC_TRANSLATION (5)
|
||||
#define CSB_CC_PROTECTION (6)
|
||||
#define CSB_CC_RD_EXTERNAL (7)
|
||||
#define CSB_CC_INVALID_OPERAND (8)
|
||||
#define CSB_CC_PRIVILEGE (9)
|
||||
#define CSB_CC_INTERNAL (10)
|
||||
#define CSB_CC_WR_EXTERNAL (12)
|
||||
#define CSB_CC_NOSPC (13)
|
||||
#define CSB_CC_EXCESSIVE_DDE (14)
|
||||
#define CSB_CC_WR_TRANSLATION (15)
|
||||
#define CSB_CC_WR_PROTECTION (16)
|
||||
#define CSB_CC_UNKNOWN_CODE (17)
|
||||
#define CSB_CC_ABORT (18)
|
||||
#define CSB_CC_TRANSPORT (20)
|
||||
#define CSB_CC_SEGMENTED_DDL (31)
|
||||
#define CSB_CC_PROGRESS_POINT (32)
|
||||
#define CSB_CC_DDE_OVERFLOW (33)
|
||||
#define CSB_CC_SESSION (34)
|
||||
#define CSB_CC_PROVISION (36)
|
||||
#define CSB_CC_CHAIN (37)
|
||||
#define CSB_CC_SEQUENCE (38)
|
||||
#define CSB_CC_HW (39)
|
||||
|
||||
#define CSB_SIZE (0x10)
|
||||
#define CSB_ALIGN CSB_SIZE
|
||||
|
||||
struct coprocessor_status_block {
|
||||
u8 flags;
|
||||
u8 cs;
|
||||
u8 cc;
|
||||
u8 ce;
|
||||
__be32 count;
|
||||
__be64 address;
|
||||
} __packed __aligned(CSB_ALIGN);
|
||||
|
||||
|
||||
/* Chapter 6.5.10 Data-Descriptor List (DDL)
|
||||
* each list contains one or more Data-Descriptor Entries (DDE)
|
||||
*/
|
||||
|
||||
#define DDE_P (0x8000)
|
||||
|
||||
#define DDE_SIZE (0x10)
|
||||
#define DDE_ALIGN DDE_SIZE
|
||||
|
||||
struct data_descriptor_entry {
|
||||
__be16 flags;
|
||||
u8 count;
|
||||
u8 index;
|
||||
__be32 length;
|
||||
__be64 address;
|
||||
} __packed __aligned(DDE_ALIGN);
|
||||
|
||||
|
||||
/* Chapter 6.5.2 Coprocessor-Request Block (CRB) */
|
||||
|
||||
#define CRB_SIZE (0x80)
|
||||
#define CRB_ALIGN (0x100) /* Errata: requires 256 alignment */
|
||||
|
||||
/* Coprocessor Status Block field
|
||||
* ADDRESS address of CSB
|
||||
* C CCB is valid
|
||||
* AT 0 = addrs are virtual, 1 = addrs are phys
|
||||
* M enable perf monitor
|
||||
*/
|
||||
#define CRB_CSB_ADDRESS (0xfffffffffffffff0)
|
||||
#define CRB_CSB_C (0x0000000000000008)
|
||||
#define CRB_CSB_AT (0x0000000000000002)
|
||||
#define CRB_CSB_M (0x0000000000000001)
|
||||
|
||||
struct coprocessor_request_block {
|
||||
__be32 ccw;
|
||||
__be32 flags;
|
||||
__be64 csb_addr;
|
||||
|
||||
struct data_descriptor_entry source;
|
||||
struct data_descriptor_entry target;
|
||||
|
||||
struct coprocessor_completion_block ccb;
|
||||
|
||||
u8 reserved[48];
|
||||
|
||||
struct coprocessor_status_block csb;
|
||||
} __packed __aligned(CRB_ALIGN);
|
||||
|
||||
|
||||
/* RFC02167 Initiate Coprocessor Instructions document
|
||||
* Chapter 8.2.1.1.1 RS
|
||||
* Chapter 8.2.3 Coprocessor Directive
|
||||
* Chapter 8.2.4 Execution
|
||||
*
|
||||
* The CCW must be converted to BE before passing to icswx()
|
||||
*/
|
||||
|
||||
#define CCW_PS (0xff000000)
|
||||
#define CCW_CT (0x00ff0000)
|
||||
#define CCW_CD (0x0000ffff)
|
||||
#define CCW_CL (0x0000c000)
|
||||
|
||||
|
||||
/* RFC02167 Initiate Coprocessor Instructions document
|
||||
* Chapter 8.2.1 Initiate Coprocessor Store Word Indexed (ICSWX)
|
||||
* Chapter 8.2.4.1 Condition Register 0
|
||||
*/
|
||||
|
||||
#define ICSWX_INITIATED (0x8)
|
||||
#define ICSWX_BUSY (0x4)
|
||||
#define ICSWX_REJECTED (0x2)
|
||||
|
||||
static inline int icswx(__be32 ccw, struct coprocessor_request_block *crb)
|
||||
{
|
||||
__be64 ccw_reg = ccw;
|
||||
u32 cr;
|
||||
|
||||
__asm__ __volatile__(
|
||||
PPC_ICSWX(%1,0,%2) "\n"
|
||||
"mfcr %0\n"
|
||||
: "=r" (cr)
|
||||
: "r" (ccw_reg), "r" (crb)
|
||||
: "cr0", "memory");
|
||||
|
||||
return (int)((cr >> 28) & 0xf);
|
||||
}
|
||||
|
||||
|
||||
#endif /* _ARCH_POWERPC_INCLUDE_ASM_ICSWX_H_ */
|
@ -136,6 +136,8 @@
|
||||
#define PPC_INST_DCBAL 0x7c2005ec
|
||||
#define PPC_INST_DCBZL 0x7c2007ec
|
||||
#define PPC_INST_ICBT 0x7c00002c
|
||||
#define PPC_INST_ICSWX 0x7c00032d
|
||||
#define PPC_INST_ICSWEPX 0x7c00076d
|
||||
#define PPC_INST_ISEL 0x7c00001e
|
||||
#define PPC_INST_ISEL_MASK 0xfc00003e
|
||||
#define PPC_INST_LDARX 0x7c0000a8
|
||||
@ -403,4 +405,15 @@
|
||||
#define MFTMR(tmr, r) stringify_in_c(.long PPC_INST_MFTMR | \
|
||||
TMRN(tmr) | ___PPC_RT(r))
|
||||
|
||||
/* Coprocessor instructions */
|
||||
#define PPC_ICSWX(s, a, b) stringify_in_c(.long PPC_INST_ICSWX | \
|
||||
___PPC_RS(s) | \
|
||||
___PPC_RA(a) | \
|
||||
___PPC_RB(b))
|
||||
#define PPC_ICSWEPX(s, a, b) stringify_in_c(.long PPC_INST_ICSWEPX | \
|
||||
___PPC_RS(s) | \
|
||||
___PPC_RA(a) | \
|
||||
___PPC_RB(b))
|
||||
|
||||
|
||||
#endif /* _ASM_POWERPC_PPC_OPCODE_H */
|
||||
|
@ -800,6 +800,7 @@ int of_get_ibm_chip_id(struct device_node *np)
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
EXPORT_SYMBOL(of_get_ibm_chip_id);
|
||||
|
||||
/**
|
||||
* cpu_to_chip_id - Return the cpus chip-id
|
||||
|
@ -33,10 +33,10 @@ static int md5_sparc64_init(struct shash_desc *desc)
|
||||
{
|
||||
struct md5_state *mctx = shash_desc_ctx(desc);
|
||||
|
||||
mctx->hash[0] = cpu_to_le32(0x67452301);
|
||||
mctx->hash[1] = cpu_to_le32(0xefcdab89);
|
||||
mctx->hash[2] = cpu_to_le32(0x98badcfe);
|
||||
mctx->hash[3] = cpu_to_le32(0x10325476);
|
||||
mctx->hash[0] = cpu_to_le32(MD5_H0);
|
||||
mctx->hash[1] = cpu_to_le32(MD5_H1);
|
||||
mctx->hash[2] = cpu_to_le32(MD5_H2);
|
||||
mctx->hash[3] = cpu_to_le32(MD5_H3);
|
||||
mctx->byte_count = 0;
|
||||
|
||||
return 0;
|
||||
|
@ -44,15 +44,19 @@
|
||||
#endif
|
||||
|
||||
|
||||
#define AESNI_ALIGN 16
|
||||
#define AES_BLOCK_MASK (~(AES_BLOCK_SIZE - 1))
|
||||
#define RFC4106_HASH_SUBKEY_SIZE 16
|
||||
|
||||
/* This data is stored at the end of the crypto_tfm struct.
|
||||
* It's a type of per "session" data storage location.
|
||||
* This needs to be 16 byte aligned.
|
||||
*/
|
||||
struct aesni_rfc4106_gcm_ctx {
|
||||
u8 hash_subkey[16];
|
||||
struct crypto_aes_ctx aes_key_expanded;
|
||||
u8 hash_subkey[16] __attribute__ ((__aligned__(AESNI_ALIGN)));
|
||||
struct crypto_aes_ctx aes_key_expanded
|
||||
__attribute__ ((__aligned__(AESNI_ALIGN)));
|
||||
u8 nonce[4];
|
||||
struct cryptd_aead *cryptd_tfm;
|
||||
};
|
||||
|
||||
struct aesni_gcm_set_hash_subkey_result {
|
||||
@ -66,10 +70,6 @@ struct aesni_hash_subkey_req_data {
|
||||
struct scatterlist sg;
|
||||
};
|
||||
|
||||
#define AESNI_ALIGN (16)
|
||||
#define AES_BLOCK_MASK (~(AES_BLOCK_SIZE-1))
|
||||
#define RFC4106_HASH_SUBKEY_SIZE 16
|
||||
|
||||
struct aesni_lrw_ctx {
|
||||
struct lrw_table_ctx lrw_table;
|
||||
u8 raw_aes_ctx[sizeof(struct crypto_aes_ctx) + AESNI_ALIGN - 1];
|
||||
@ -283,10 +283,11 @@ static void (*aesni_gcm_dec_tfm)(void *ctx, u8 *out,
|
||||
static inline struct
|
||||
aesni_rfc4106_gcm_ctx *aesni_rfc4106_gcm_ctx_get(struct crypto_aead *tfm)
|
||||
{
|
||||
return
|
||||
(struct aesni_rfc4106_gcm_ctx *)
|
||||
PTR_ALIGN((u8 *)
|
||||
crypto_tfm_ctx(crypto_aead_tfm(tfm)), AESNI_ALIGN);
|
||||
unsigned long align = AESNI_ALIGN;
|
||||
|
||||
if (align <= crypto_tfm_ctx_alignment())
|
||||
align = 1;
|
||||
return PTR_ALIGN(crypto_aead_ctx(tfm), align);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -790,36 +791,30 @@ static int xts_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst,
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
static int rfc4106_init(struct crypto_tfm *tfm)
|
||||
static int rfc4106_init(struct crypto_aead *aead)
|
||||
{
|
||||
struct cryptd_aead *cryptd_tfm;
|
||||
struct aesni_rfc4106_gcm_ctx *ctx = (struct aesni_rfc4106_gcm_ctx *)
|
||||
PTR_ALIGN((u8 *)crypto_tfm_ctx(tfm), AESNI_ALIGN);
|
||||
struct crypto_aead *cryptd_child;
|
||||
struct aesni_rfc4106_gcm_ctx *child_ctx;
|
||||
struct cryptd_aead **ctx = crypto_aead_ctx(aead);
|
||||
|
||||
cryptd_tfm = cryptd_alloc_aead("__driver-gcm-aes-aesni",
|
||||
CRYPTO_ALG_INTERNAL,
|
||||
CRYPTO_ALG_INTERNAL);
|
||||
if (IS_ERR(cryptd_tfm))
|
||||
return PTR_ERR(cryptd_tfm);
|
||||
|
||||
cryptd_child = cryptd_aead_child(cryptd_tfm);
|
||||
child_ctx = aesni_rfc4106_gcm_ctx_get(cryptd_child);
|
||||
memcpy(child_ctx, ctx, sizeof(*ctx));
|
||||
ctx->cryptd_tfm = cryptd_tfm;
|
||||
tfm->crt_aead.reqsize = sizeof(struct aead_request)
|
||||
+ crypto_aead_reqsize(&cryptd_tfm->base);
|
||||
*ctx = cryptd_tfm;
|
||||
crypto_aead_set_reqsize(
|
||||
aead,
|
||||
sizeof(struct aead_request) +
|
||||
crypto_aead_reqsize(&cryptd_tfm->base));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rfc4106_exit(struct crypto_tfm *tfm)
|
||||
static void rfc4106_exit(struct crypto_aead *aead)
|
||||
{
|
||||
struct aesni_rfc4106_gcm_ctx *ctx =
|
||||
(struct aesni_rfc4106_gcm_ctx *)
|
||||
PTR_ALIGN((u8 *)crypto_tfm_ctx(tfm), AESNI_ALIGN);
|
||||
if (!IS_ERR(ctx->cryptd_tfm))
|
||||
cryptd_free_aead(ctx->cryptd_tfm);
|
||||
return;
|
||||
struct cryptd_aead **ctx = crypto_aead_ctx(aead);
|
||||
|
||||
cryptd_free_aead(*ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -845,8 +840,6 @@ rfc4106_set_hash_subkey(u8 *hash_subkey, const u8 *key, unsigned int key_len)
|
||||
if (IS_ERR(ctr_tfm))
|
||||
return PTR_ERR(ctr_tfm);
|
||||
|
||||
crypto_ablkcipher_clear_flags(ctr_tfm, ~0);
|
||||
|
||||
ret = crypto_ablkcipher_setkey(ctr_tfm, key, key_len);
|
||||
if (ret)
|
||||
goto out_free_ablkcipher;
|
||||
@ -895,73 +888,29 @@ out_free_ablkcipher:
|
||||
static int common_rfc4106_set_key(struct crypto_aead *aead, const u8 *key,
|
||||
unsigned int key_len)
|
||||
{
|
||||
int ret = 0;
|
||||
struct crypto_tfm *tfm = crypto_aead_tfm(aead);
|
||||
struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(aead);
|
||||
u8 *new_key_align, *new_key_mem = NULL;
|
||||
|
||||
if (key_len < 4) {
|
||||
crypto_tfm_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
|
||||
crypto_aead_set_flags(aead, CRYPTO_TFM_RES_BAD_KEY_LEN);
|
||||
return -EINVAL;
|
||||
}
|
||||
/*Account for 4 byte nonce at the end.*/
|
||||
key_len -= 4;
|
||||
if (key_len != AES_KEYSIZE_128 && key_len != AES_KEYSIZE_192 &&
|
||||
key_len != AES_KEYSIZE_256) {
|
||||
crypto_tfm_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(ctx->nonce, key + key_len, sizeof(ctx->nonce));
|
||||
/*This must be on a 16 byte boundary!*/
|
||||
if ((unsigned long)(&(ctx->aes_key_expanded.key_enc[0])) % AESNI_ALIGN)
|
||||
return -EINVAL;
|
||||
|
||||
if ((unsigned long)key % AESNI_ALIGN) {
|
||||
/*key is not aligned: use an auxuliar aligned pointer*/
|
||||
new_key_mem = kmalloc(key_len+AESNI_ALIGN, GFP_KERNEL);
|
||||
if (!new_key_mem)
|
||||
return -ENOMEM;
|
||||
|
||||
new_key_align = PTR_ALIGN(new_key_mem, AESNI_ALIGN);
|
||||
memcpy(new_key_align, key, key_len);
|
||||
key = new_key_align;
|
||||
}
|
||||
|
||||
if (!irq_fpu_usable())
|
||||
ret = crypto_aes_expand_key(&(ctx->aes_key_expanded),
|
||||
key, key_len);
|
||||
else {
|
||||
kernel_fpu_begin();
|
||||
ret = aesni_set_key(&(ctx->aes_key_expanded), key, key_len);
|
||||
kernel_fpu_end();
|
||||
}
|
||||
/*This must be on a 16 byte boundary!*/
|
||||
if ((unsigned long)(&(ctx->hash_subkey[0])) % AESNI_ALIGN) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
ret = rfc4106_set_hash_subkey(ctx->hash_subkey, key, key_len);
|
||||
exit:
|
||||
kfree(new_key_mem);
|
||||
return ret;
|
||||
return aes_set_key_common(crypto_aead_tfm(aead),
|
||||
&ctx->aes_key_expanded, key, key_len) ?:
|
||||
rfc4106_set_hash_subkey(ctx->hash_subkey, key, key_len);
|
||||
}
|
||||
|
||||
static int rfc4106_set_key(struct crypto_aead *parent, const u8 *key,
|
||||
unsigned int key_len)
|
||||
{
|
||||
struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(parent);
|
||||
struct crypto_aead *child = cryptd_aead_child(ctx->cryptd_tfm);
|
||||
struct aesni_rfc4106_gcm_ctx *c_ctx = aesni_rfc4106_gcm_ctx_get(child);
|
||||
struct cryptd_aead *cryptd_tfm = ctx->cryptd_tfm;
|
||||
int ret;
|
||||
struct cryptd_aead **ctx = crypto_aead_ctx(parent);
|
||||
struct cryptd_aead *cryptd_tfm = *ctx;
|
||||
|
||||
ret = crypto_aead_setkey(child, key, key_len);
|
||||
if (!ret) {
|
||||
memcpy(ctx, c_ctx, sizeof(*ctx));
|
||||
ctx->cryptd_tfm = cryptd_tfm;
|
||||
}
|
||||
return ret;
|
||||
return crypto_aead_setkey(&cryptd_tfm->base, key, key_len);
|
||||
}
|
||||
|
||||
static int common_rfc4106_set_authsize(struct crypto_aead *aead,
|
||||
@ -975,7 +924,7 @@ static int common_rfc4106_set_authsize(struct crypto_aead *aead,
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
crypto_aead_crt(aead)->authsize = authsize;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -984,30 +933,23 @@ static int common_rfc4106_set_authsize(struct crypto_aead *aead,
|
||||
static int rfc4106_set_authsize(struct crypto_aead *parent,
|
||||
unsigned int authsize)
|
||||
{
|
||||
struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(parent);
|
||||
struct crypto_aead *child = cryptd_aead_child(ctx->cryptd_tfm);
|
||||
int ret;
|
||||
struct cryptd_aead **ctx = crypto_aead_ctx(parent);
|
||||
struct cryptd_aead *cryptd_tfm = *ctx;
|
||||
|
||||
ret = crypto_aead_setauthsize(child, authsize);
|
||||
if (!ret)
|
||||
crypto_aead_crt(parent)->authsize = authsize;
|
||||
return ret;
|
||||
return crypto_aead_setauthsize(&cryptd_tfm->base, authsize);
|
||||
}
|
||||
|
||||
static int __driver_rfc4106_encrypt(struct aead_request *req)
|
||||
static int helper_rfc4106_encrypt(struct aead_request *req)
|
||||
{
|
||||
u8 one_entry_in_sg = 0;
|
||||
u8 *src, *dst, *assoc;
|
||||
__be32 counter = cpu_to_be32(1);
|
||||
struct crypto_aead *tfm = crypto_aead_reqtfm(req);
|
||||
struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm);
|
||||
u32 key_len = ctx->aes_key_expanded.key_length;
|
||||
void *aes_ctx = &(ctx->aes_key_expanded);
|
||||
unsigned long auth_tag_len = crypto_aead_authsize(tfm);
|
||||
u8 iv_tab[16+AESNI_ALIGN];
|
||||
u8* iv = (u8 *) PTR_ALIGN((u8 *)iv_tab, AESNI_ALIGN);
|
||||
u8 iv[16] __attribute__ ((__aligned__(AESNI_ALIGN)));
|
||||
struct scatter_walk src_sg_walk;
|
||||
struct scatter_walk assoc_sg_walk;
|
||||
struct scatter_walk dst_sg_walk;
|
||||
unsigned int i;
|
||||
|
||||
@ -1016,12 +958,6 @@ static int __driver_rfc4106_encrypt(struct aead_request *req)
|
||||
/* to 8 or 12 bytes */
|
||||
if (unlikely(req->assoclen != 8 && req->assoclen != 12))
|
||||
return -EINVAL;
|
||||
if (unlikely(auth_tag_len != 8 && auth_tag_len != 12 && auth_tag_len != 16))
|
||||
return -EINVAL;
|
||||
if (unlikely(key_len != AES_KEYSIZE_128 &&
|
||||
key_len != AES_KEYSIZE_192 &&
|
||||
key_len != AES_KEYSIZE_256))
|
||||
return -EINVAL;
|
||||
|
||||
/* IV below built */
|
||||
for (i = 0; i < 4; i++)
|
||||
@ -1030,55 +966,57 @@ static int __driver_rfc4106_encrypt(struct aead_request *req)
|
||||
*(iv+4+i) = req->iv[i];
|
||||
*((__be32 *)(iv+12)) = counter;
|
||||
|
||||
if ((sg_is_last(req->src)) && (sg_is_last(req->assoc))) {
|
||||
if (sg_is_last(req->src) &&
|
||||
req->src->offset + req->src->length <= PAGE_SIZE &&
|
||||
sg_is_last(req->dst) &&
|
||||
req->dst->offset + req->dst->length <= PAGE_SIZE) {
|
||||
one_entry_in_sg = 1;
|
||||
scatterwalk_start(&src_sg_walk, req->src);
|
||||
scatterwalk_start(&assoc_sg_walk, req->assoc);
|
||||
src = scatterwalk_map(&src_sg_walk);
|
||||
assoc = scatterwalk_map(&assoc_sg_walk);
|
||||
assoc = scatterwalk_map(&src_sg_walk);
|
||||
src = assoc + req->assoclen;
|
||||
dst = src;
|
||||
if (unlikely(req->src != req->dst)) {
|
||||
scatterwalk_start(&dst_sg_walk, req->dst);
|
||||
dst = scatterwalk_map(&dst_sg_walk);
|
||||
dst = scatterwalk_map(&dst_sg_walk) + req->assoclen;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* Allocate memory for src, dst, assoc */
|
||||
src = kmalloc(req->cryptlen + auth_tag_len + req->assoclen,
|
||||
assoc = kmalloc(req->cryptlen + auth_tag_len + req->assoclen,
|
||||
GFP_ATOMIC);
|
||||
if (unlikely(!src))
|
||||
if (unlikely(!assoc))
|
||||
return -ENOMEM;
|
||||
assoc = (src + req->cryptlen + auth_tag_len);
|
||||
scatterwalk_map_and_copy(src, req->src, 0, req->cryptlen, 0);
|
||||
scatterwalk_map_and_copy(assoc, req->assoc, 0,
|
||||
req->assoclen, 0);
|
||||
scatterwalk_map_and_copy(assoc, req->src, 0,
|
||||
req->assoclen + req->cryptlen, 0);
|
||||
src = assoc + req->assoclen;
|
||||
dst = src;
|
||||
}
|
||||
|
||||
kernel_fpu_begin();
|
||||
aesni_gcm_enc_tfm(aes_ctx, dst, src, (unsigned long)req->cryptlen, iv,
|
||||
ctx->hash_subkey, assoc, (unsigned long)req->assoclen, dst
|
||||
+ ((unsigned long)req->cryptlen), auth_tag_len);
|
||||
kernel_fpu_end();
|
||||
|
||||
/* The authTag (aka the Integrity Check Value) needs to be written
|
||||
* back to the packet. */
|
||||
if (one_entry_in_sg) {
|
||||
if (unlikely(req->src != req->dst)) {
|
||||
scatterwalk_unmap(dst);
|
||||
scatterwalk_done(&dst_sg_walk, 0, 0);
|
||||
scatterwalk_unmap(dst - req->assoclen);
|
||||
scatterwalk_advance(&dst_sg_walk, req->dst->length);
|
||||
scatterwalk_done(&dst_sg_walk, 1, 0);
|
||||
}
|
||||
scatterwalk_unmap(src);
|
||||
scatterwalk_unmap(assoc);
|
||||
scatterwalk_done(&src_sg_walk, 0, 0);
|
||||
scatterwalk_done(&assoc_sg_walk, 0, 0);
|
||||
scatterwalk_advance(&src_sg_walk, req->src->length);
|
||||
scatterwalk_done(&src_sg_walk, req->src == req->dst, 0);
|
||||
} else {
|
||||
scatterwalk_map_and_copy(dst, req->dst, 0,
|
||||
req->cryptlen + auth_tag_len, 1);
|
||||
kfree(src);
|
||||
scatterwalk_map_and_copy(dst, req->dst, req->assoclen,
|
||||
req->cryptlen + auth_tag_len, 1);
|
||||
kfree(assoc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __driver_rfc4106_decrypt(struct aead_request *req)
|
||||
static int helper_rfc4106_decrypt(struct aead_request *req)
|
||||
{
|
||||
u8 one_entry_in_sg = 0;
|
||||
u8 *src, *dst, *assoc;
|
||||
@ -1087,26 +1025,16 @@ static int __driver_rfc4106_decrypt(struct aead_request *req)
|
||||
int retval = 0;
|
||||
struct crypto_aead *tfm = crypto_aead_reqtfm(req);
|
||||
struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm);
|
||||
u32 key_len = ctx->aes_key_expanded.key_length;
|
||||
void *aes_ctx = &(ctx->aes_key_expanded);
|
||||
unsigned long auth_tag_len = crypto_aead_authsize(tfm);
|
||||
u8 iv_and_authTag[32+AESNI_ALIGN];
|
||||
u8 *iv = (u8 *) PTR_ALIGN((u8 *)iv_and_authTag, AESNI_ALIGN);
|
||||
u8 *authTag = iv + 16;
|
||||
u8 iv[16] __attribute__ ((__aligned__(AESNI_ALIGN)));
|
||||
u8 authTag[16];
|
||||
struct scatter_walk src_sg_walk;
|
||||
struct scatter_walk assoc_sg_walk;
|
||||
struct scatter_walk dst_sg_walk;
|
||||
unsigned int i;
|
||||
|
||||
if (unlikely((req->cryptlen < auth_tag_len) ||
|
||||
(req->assoclen != 8 && req->assoclen != 12)))
|
||||
if (unlikely(req->assoclen != 8 && req->assoclen != 12))
|
||||
return -EINVAL;
|
||||
if (unlikely(auth_tag_len != 8 && auth_tag_len != 12 && auth_tag_len != 16))
|
||||
return -EINVAL;
|
||||
if (unlikely(key_len != AES_KEYSIZE_128 &&
|
||||
key_len != AES_KEYSIZE_192 &&
|
||||
key_len != AES_KEYSIZE_256))
|
||||
return -EINVAL;
|
||||
|
||||
/* Assuming we are supporting rfc4106 64-bit extended */
|
||||
/* sequence numbers We need to have the AAD length */
|
||||
@ -1120,33 +1048,36 @@ static int __driver_rfc4106_decrypt(struct aead_request *req)
|
||||
*(iv+4+i) = req->iv[i];
|
||||
*((__be32 *)(iv+12)) = counter;
|
||||
|
||||
if ((sg_is_last(req->src)) && (sg_is_last(req->assoc))) {
|
||||
if (sg_is_last(req->src) &&
|
||||
req->src->offset + req->src->length <= PAGE_SIZE &&
|
||||
sg_is_last(req->dst) &&
|
||||
req->dst->offset + req->dst->length <= PAGE_SIZE) {
|
||||
one_entry_in_sg = 1;
|
||||
scatterwalk_start(&src_sg_walk, req->src);
|
||||
scatterwalk_start(&assoc_sg_walk, req->assoc);
|
||||
src = scatterwalk_map(&src_sg_walk);
|
||||
assoc = scatterwalk_map(&assoc_sg_walk);
|
||||
assoc = scatterwalk_map(&src_sg_walk);
|
||||
src = assoc + req->assoclen;
|
||||
dst = src;
|
||||
if (unlikely(req->src != req->dst)) {
|
||||
scatterwalk_start(&dst_sg_walk, req->dst);
|
||||
dst = scatterwalk_map(&dst_sg_walk);
|
||||
dst = scatterwalk_map(&dst_sg_walk) + req->assoclen;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* Allocate memory for src, dst, assoc */
|
||||
src = kmalloc(req->cryptlen + req->assoclen, GFP_ATOMIC);
|
||||
if (!src)
|
||||
assoc = kmalloc(req->cryptlen + req->assoclen, GFP_ATOMIC);
|
||||
if (!assoc)
|
||||
return -ENOMEM;
|
||||
assoc = (src + req->cryptlen);
|
||||
scatterwalk_map_and_copy(src, req->src, 0, req->cryptlen, 0);
|
||||
scatterwalk_map_and_copy(assoc, req->assoc, 0,
|
||||
req->assoclen, 0);
|
||||
scatterwalk_map_and_copy(assoc, req->src, 0,
|
||||
req->assoclen + req->cryptlen, 0);
|
||||
src = assoc + req->assoclen;
|
||||
dst = src;
|
||||
}
|
||||
|
||||
kernel_fpu_begin();
|
||||
aesni_gcm_dec_tfm(aes_ctx, dst, src, tempCipherLen, iv,
|
||||
ctx->hash_subkey, assoc, (unsigned long)req->assoclen,
|
||||
authTag, auth_tag_len);
|
||||
kernel_fpu_end();
|
||||
|
||||
/* Compare generated tag with passed in tag. */
|
||||
retval = crypto_memneq(src + tempCipherLen, authTag, auth_tag_len) ?
|
||||
@ -1154,90 +1085,59 @@ static int __driver_rfc4106_decrypt(struct aead_request *req)
|
||||
|
||||
if (one_entry_in_sg) {
|
||||
if (unlikely(req->src != req->dst)) {
|
||||
scatterwalk_unmap(dst);
|
||||
scatterwalk_done(&dst_sg_walk, 0, 0);
|
||||
scatterwalk_unmap(dst - req->assoclen);
|
||||
scatterwalk_advance(&dst_sg_walk, req->dst->length);
|
||||
scatterwalk_done(&dst_sg_walk, 1, 0);
|
||||
}
|
||||
scatterwalk_unmap(src);
|
||||
scatterwalk_unmap(assoc);
|
||||
scatterwalk_done(&src_sg_walk, 0, 0);
|
||||
scatterwalk_done(&assoc_sg_walk, 0, 0);
|
||||
scatterwalk_advance(&src_sg_walk, req->src->length);
|
||||
scatterwalk_done(&src_sg_walk, req->src == req->dst, 0);
|
||||
} else {
|
||||
scatterwalk_map_and_copy(dst, req->dst, 0, tempCipherLen, 1);
|
||||
kfree(src);
|
||||
scatterwalk_map_and_copy(dst, req->dst, req->assoclen,
|
||||
tempCipherLen, 1);
|
||||
kfree(assoc);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int rfc4106_encrypt(struct aead_request *req)
|
||||
{
|
||||
int ret;
|
||||
struct crypto_aead *tfm = crypto_aead_reqtfm(req);
|
||||
struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm);
|
||||
struct cryptd_aead **ctx = crypto_aead_ctx(tfm);
|
||||
struct cryptd_aead *cryptd_tfm = *ctx;
|
||||
struct aead_request *subreq = aead_request_ctx(req);
|
||||
|
||||
if (!irq_fpu_usable()) {
|
||||
struct aead_request *cryptd_req =
|
||||
(struct aead_request *) aead_request_ctx(req);
|
||||
aead_request_set_tfm(subreq, irq_fpu_usable() ?
|
||||
cryptd_aead_child(cryptd_tfm) :
|
||||
&cryptd_tfm->base);
|
||||
|
||||
memcpy(cryptd_req, req, sizeof(*req));
|
||||
aead_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base);
|
||||
ret = crypto_aead_encrypt(cryptd_req);
|
||||
} else {
|
||||
kernel_fpu_begin();
|
||||
ret = __driver_rfc4106_encrypt(req);
|
||||
kernel_fpu_end();
|
||||
}
|
||||
return ret;
|
||||
aead_request_set_callback(subreq, req->base.flags,
|
||||
req->base.complete, req->base.data);
|
||||
aead_request_set_crypt(subreq, req->src, req->dst,
|
||||
req->cryptlen, req->iv);
|
||||
aead_request_set_ad(subreq, req->assoclen);
|
||||
|
||||
return crypto_aead_encrypt(subreq);
|
||||
}
|
||||
|
||||
static int rfc4106_decrypt(struct aead_request *req)
|
||||
{
|
||||
int ret;
|
||||
struct crypto_aead *tfm = crypto_aead_reqtfm(req);
|
||||
struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm);
|
||||
struct cryptd_aead **ctx = crypto_aead_ctx(tfm);
|
||||
struct cryptd_aead *cryptd_tfm = *ctx;
|
||||
struct aead_request *subreq = aead_request_ctx(req);
|
||||
|
||||
if (!irq_fpu_usable()) {
|
||||
struct aead_request *cryptd_req =
|
||||
(struct aead_request *) aead_request_ctx(req);
|
||||
aead_request_set_tfm(subreq, irq_fpu_usable() ?
|
||||
cryptd_aead_child(cryptd_tfm) :
|
||||
&cryptd_tfm->base);
|
||||
|
||||
memcpy(cryptd_req, req, sizeof(*req));
|
||||
aead_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base);
|
||||
ret = crypto_aead_decrypt(cryptd_req);
|
||||
} else {
|
||||
kernel_fpu_begin();
|
||||
ret = __driver_rfc4106_decrypt(req);
|
||||
kernel_fpu_end();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
aead_request_set_callback(subreq, req->base.flags,
|
||||
req->base.complete, req->base.data);
|
||||
aead_request_set_crypt(subreq, req->src, req->dst,
|
||||
req->cryptlen, req->iv);
|
||||
aead_request_set_ad(subreq, req->assoclen);
|
||||
|
||||
static int helper_rfc4106_encrypt(struct aead_request *req)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (unlikely(!irq_fpu_usable())) {
|
||||
WARN_ONCE(1, "__gcm-aes-aesni alg used in invalid context");
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
kernel_fpu_begin();
|
||||
ret = __driver_rfc4106_encrypt(req);
|
||||
kernel_fpu_end();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int helper_rfc4106_decrypt(struct aead_request *req)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (unlikely(!irq_fpu_usable())) {
|
||||
WARN_ONCE(1, "__gcm-aes-aesni alg used in invalid context");
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
kernel_fpu_begin();
|
||||
ret = __driver_rfc4106_decrypt(req);
|
||||
kernel_fpu_end();
|
||||
}
|
||||
return ret;
|
||||
return crypto_aead_decrypt(subreq);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1410,51 +1310,6 @@ static struct crypto_alg aesni_algs[] = { {
|
||||
.geniv = "chainiv",
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.cra_name = "__gcm-aes-aesni",
|
||||
.cra_driver_name = "__driver-gcm-aes-aesni",
|
||||
.cra_priority = 0,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_INTERNAL,
|
||||
.cra_blocksize = 1,
|
||||
.cra_ctxsize = sizeof(struct aesni_rfc4106_gcm_ctx) +
|
||||
AESNI_ALIGN,
|
||||
.cra_alignmask = 0,
|
||||
.cra_type = &crypto_aead_type,
|
||||
.cra_module = THIS_MODULE,
|
||||
.cra_u = {
|
||||
.aead = {
|
||||
.setkey = common_rfc4106_set_key,
|
||||
.setauthsize = common_rfc4106_set_authsize,
|
||||
.encrypt = helper_rfc4106_encrypt,
|
||||
.decrypt = helper_rfc4106_decrypt,
|
||||
.ivsize = 8,
|
||||
.maxauthsize = 16,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.cra_name = "rfc4106(gcm(aes))",
|
||||
.cra_driver_name = "rfc4106-gcm-aesni",
|
||||
.cra_priority = 400,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC,
|
||||
.cra_blocksize = 1,
|
||||
.cra_ctxsize = sizeof(struct aesni_rfc4106_gcm_ctx) +
|
||||
AESNI_ALIGN,
|
||||
.cra_alignmask = 0,
|
||||
.cra_type = &crypto_nivaead_type,
|
||||
.cra_module = THIS_MODULE,
|
||||
.cra_init = rfc4106_init,
|
||||
.cra_exit = rfc4106_exit,
|
||||
.cra_u = {
|
||||
.aead = {
|
||||
.setkey = rfc4106_set_key,
|
||||
.setauthsize = rfc4106_set_authsize,
|
||||
.encrypt = rfc4106_encrypt,
|
||||
.decrypt = rfc4106_decrypt,
|
||||
.geniv = "seqiv",
|
||||
.ivsize = 8,
|
||||
.maxauthsize = 16,
|
||||
},
|
||||
},
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_CRYPTO_PCBC)
|
||||
}, {
|
||||
@ -1569,6 +1424,46 @@ static struct crypto_alg aesni_algs[] = { {
|
||||
},
|
||||
} };
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
static struct aead_alg aesni_aead_algs[] = { {
|
||||
.setkey = common_rfc4106_set_key,
|
||||
.setauthsize = common_rfc4106_set_authsize,
|
||||
.encrypt = helper_rfc4106_encrypt,
|
||||
.decrypt = helper_rfc4106_decrypt,
|
||||
.ivsize = 8,
|
||||
.maxauthsize = 16,
|
||||
.base = {
|
||||
.cra_name = "__gcm-aes-aesni",
|
||||
.cra_driver_name = "__driver-gcm-aes-aesni",
|
||||
.cra_flags = CRYPTO_ALG_INTERNAL,
|
||||
.cra_blocksize = 1,
|
||||
.cra_ctxsize = sizeof(struct aesni_rfc4106_gcm_ctx),
|
||||
.cra_alignmask = AESNI_ALIGN - 1,
|
||||
.cra_module = THIS_MODULE,
|
||||
},
|
||||
}, {
|
||||
.init = rfc4106_init,
|
||||
.exit = rfc4106_exit,
|
||||
.setkey = rfc4106_set_key,
|
||||
.setauthsize = rfc4106_set_authsize,
|
||||
.encrypt = rfc4106_encrypt,
|
||||
.decrypt = rfc4106_decrypt,
|
||||
.ivsize = 8,
|
||||
.maxauthsize = 16,
|
||||
.base = {
|
||||
.cra_name = "rfc4106(gcm(aes))",
|
||||
.cra_driver_name = "rfc4106-gcm-aesni",
|
||||
.cra_priority = 400,
|
||||
.cra_flags = CRYPTO_ALG_ASYNC,
|
||||
.cra_blocksize = 1,
|
||||
.cra_ctxsize = sizeof(struct cryptd_aead *),
|
||||
.cra_module = THIS_MODULE,
|
||||
},
|
||||
} };
|
||||
#else
|
||||
static struct aead_alg aesni_aead_algs[0];
|
||||
#endif
|
||||
|
||||
|
||||
static const struct x86_cpu_id aesni_cpu_id[] = {
|
||||
X86_FEATURE_MATCH(X86_FEATURE_AES),
|
||||
@ -1616,11 +1511,27 @@ static int __init aesni_init(void)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return crypto_register_algs(aesni_algs, ARRAY_SIZE(aesni_algs));
|
||||
err = crypto_register_algs(aesni_algs, ARRAY_SIZE(aesni_algs));
|
||||
if (err)
|
||||
goto fpu_exit;
|
||||
|
||||
err = crypto_register_aeads(aesni_aead_algs,
|
||||
ARRAY_SIZE(aesni_aead_algs));
|
||||
if (err)
|
||||
goto unregister_algs;
|
||||
|
||||
return err;
|
||||
|
||||
unregister_algs:
|
||||
crypto_unregister_algs(aesni_algs, ARRAY_SIZE(aesni_algs));
|
||||
fpu_exit:
|
||||
crypto_fpu_exit();
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit aesni_exit(void)
|
||||
{
|
||||
crypto_unregister_aeads(aesni_aead_algs, ARRAY_SIZE(aesni_aead_algs));
|
||||
crypto_unregister_algs(aesni_algs, ARRAY_SIZE(aesni_algs));
|
||||
|
||||
crypto_fpu_exit();
|
||||
|
@ -156,7 +156,7 @@ int __init crypto_fpu_init(void)
|
||||
return crypto_register_template(&crypto_fpu_tmpl);
|
||||
}
|
||||
|
||||
void __exit crypto_fpu_exit(void)
|
||||
void crypto_fpu_exit(void)
|
||||
{
|
||||
crypto_unregister_template(&crypto_fpu_tmpl);
|
||||
}
|
||||
|
@ -882,7 +882,8 @@ static int __init sha1_mb_mod_init(void)
|
||||
INIT_DELAYED_WORK(&cpu_state->flush, mcryptd_flusher);
|
||||
cpu_state->cpu = cpu;
|
||||
cpu_state->alg_state = &sha1_mb_alg_state;
|
||||
cpu_state->mgr = (struct sha1_ctx_mgr *) kzalloc(sizeof(struct sha1_ctx_mgr), GFP_KERNEL);
|
||||
cpu_state->mgr = kzalloc(sizeof(struct sha1_ctx_mgr),
|
||||
GFP_KERNEL);
|
||||
if (!cpu_state->mgr)
|
||||
goto err2;
|
||||
sha1_ctx_mgr_init(cpu_state->mgr);
|
||||
|
174
crypto/842.c
174
crypto/842.c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Cryptographic API for the 842 compression algorithm.
|
||||
* Cryptographic API for the 842 software compression algorithm.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -11,173 +11,73 @@
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* Copyright (C) IBM Corporation, 2011-2015
|
||||
*
|
||||
* Copyright (C) IBM Corporation, 2011
|
||||
* Original Authors: Robert Jennings <rcj@linux.vnet.ibm.com>
|
||||
* Seth Jennings <sjenning@linux.vnet.ibm.com>
|
||||
*
|
||||
* Authors: Robert Jennings <rcj@linux.vnet.ibm.com>
|
||||
* Seth Jennings <sjenning@linux.vnet.ibm.com>
|
||||
* Rewrite: Dan Streetman <ddstreet@ieee.org>
|
||||
*
|
||||
* This is the software implementation of compression and decompression using
|
||||
* the 842 format. This uses the software 842 library at lib/842/ which is
|
||||
* only a reference implementation, and is very, very slow as compared to other
|
||||
* software compressors. You probably do not want to use this software
|
||||
* compression. If you have access to the PowerPC 842 compression hardware, you
|
||||
* want to use the 842 hardware compression interface, which is at:
|
||||
* drivers/crypto/nx/nx-842-crypto.c
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/nx842.h>
|
||||
#include <linux/lzo.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/sw842.h>
|
||||
|
||||
static int nx842_uselzo;
|
||||
|
||||
struct nx842_ctx {
|
||||
void *nx842_wmem; /* working memory for 842/lzo */
|
||||
struct crypto842_ctx {
|
||||
char wmem[SW842_MEM_COMPRESS]; /* working memory for compress */
|
||||
};
|
||||
|
||||
enum nx842_crypto_type {
|
||||
NX842_CRYPTO_TYPE_842,
|
||||
NX842_CRYPTO_TYPE_LZO
|
||||
};
|
||||
|
||||
#define NX842_SENTINEL 0xdeadbeef
|
||||
|
||||
struct nx842_crypto_header {
|
||||
unsigned int sentinel; /* debug */
|
||||
enum nx842_crypto_type type;
|
||||
};
|
||||
|
||||
static int nx842_init(struct crypto_tfm *tfm)
|
||||
static int crypto842_compress(struct crypto_tfm *tfm,
|
||||
const u8 *src, unsigned int slen,
|
||||
u8 *dst, unsigned int *dlen)
|
||||
{
|
||||
struct nx842_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
int wmemsize;
|
||||
struct crypto842_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
|
||||
wmemsize = max_t(int, nx842_get_workmem_size(), LZO1X_MEM_COMPRESS);
|
||||
ctx->nx842_wmem = kmalloc(wmemsize, GFP_NOFS);
|
||||
if (!ctx->nx842_wmem)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
return sw842_compress(src, slen, dst, dlen, ctx->wmem);
|
||||
}
|
||||
|
||||
static void nx842_exit(struct crypto_tfm *tfm)
|
||||
static int crypto842_decompress(struct crypto_tfm *tfm,
|
||||
const u8 *src, unsigned int slen,
|
||||
u8 *dst, unsigned int *dlen)
|
||||
{
|
||||
struct nx842_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
|
||||
kfree(ctx->nx842_wmem);
|
||||
}
|
||||
|
||||
static void nx842_reset_uselzo(unsigned long data)
|
||||
{
|
||||
nx842_uselzo = 0;
|
||||
}
|
||||
|
||||
static DEFINE_TIMER(failover_timer, nx842_reset_uselzo, 0, 0);
|
||||
|
||||
static int nx842_crypto_compress(struct crypto_tfm *tfm, const u8 *src,
|
||||
unsigned int slen, u8 *dst, unsigned int *dlen)
|
||||
{
|
||||
struct nx842_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
struct nx842_crypto_header *hdr;
|
||||
unsigned int tmp_len = *dlen;
|
||||
size_t lzodlen; /* needed for lzo */
|
||||
int err;
|
||||
|
||||
*dlen = 0;
|
||||
hdr = (struct nx842_crypto_header *)dst;
|
||||
hdr->sentinel = NX842_SENTINEL; /* debug */
|
||||
dst += sizeof(struct nx842_crypto_header);
|
||||
tmp_len -= sizeof(struct nx842_crypto_header);
|
||||
lzodlen = tmp_len;
|
||||
|
||||
if (likely(!nx842_uselzo)) {
|
||||
err = nx842_compress(src, slen, dst, &tmp_len, ctx->nx842_wmem);
|
||||
|
||||
if (likely(!err)) {
|
||||
hdr->type = NX842_CRYPTO_TYPE_842;
|
||||
*dlen = tmp_len + sizeof(struct nx842_crypto_header);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* hardware failed */
|
||||
nx842_uselzo = 1;
|
||||
|
||||
/* set timer to check for hardware again in 1 second */
|
||||
mod_timer(&failover_timer, jiffies + msecs_to_jiffies(1000));
|
||||
}
|
||||
|
||||
/* no hardware, use lzo */
|
||||
err = lzo1x_1_compress(src, slen, dst, &lzodlen, ctx->nx842_wmem);
|
||||
if (err != LZO_E_OK)
|
||||
return -EINVAL;
|
||||
|
||||
hdr->type = NX842_CRYPTO_TYPE_LZO;
|
||||
*dlen = lzodlen + sizeof(struct nx842_crypto_header);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nx842_crypto_decompress(struct crypto_tfm *tfm, const u8 *src,
|
||||
unsigned int slen, u8 *dst, unsigned int *dlen)
|
||||
{
|
||||
struct nx842_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
struct nx842_crypto_header *hdr;
|
||||
unsigned int tmp_len = *dlen;
|
||||
size_t lzodlen; /* needed for lzo */
|
||||
int err;
|
||||
|
||||
*dlen = 0;
|
||||
hdr = (struct nx842_crypto_header *)src;
|
||||
|
||||
if (unlikely(hdr->sentinel != NX842_SENTINEL))
|
||||
return -EINVAL;
|
||||
|
||||
src += sizeof(struct nx842_crypto_header);
|
||||
slen -= sizeof(struct nx842_crypto_header);
|
||||
|
||||
if (likely(hdr->type == NX842_CRYPTO_TYPE_842)) {
|
||||
err = nx842_decompress(src, slen, dst, &tmp_len,
|
||||
ctx->nx842_wmem);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
*dlen = tmp_len;
|
||||
} else if (hdr->type == NX842_CRYPTO_TYPE_LZO) {
|
||||
lzodlen = tmp_len;
|
||||
err = lzo1x_decompress_safe(src, slen, dst, &lzodlen);
|
||||
if (err != LZO_E_OK)
|
||||
return -EINVAL;
|
||||
*dlen = lzodlen;
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
return sw842_decompress(src, slen, dst, dlen);
|
||||
}
|
||||
|
||||
static struct crypto_alg alg = {
|
||||
.cra_name = "842",
|
||||
.cra_driver_name = "842-generic",
|
||||
.cra_priority = 100,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_COMPRESS,
|
||||
.cra_ctxsize = sizeof(struct nx842_ctx),
|
||||
.cra_ctxsize = sizeof(struct crypto842_ctx),
|
||||
.cra_module = THIS_MODULE,
|
||||
.cra_init = nx842_init,
|
||||
.cra_exit = nx842_exit,
|
||||
.cra_u = { .compress = {
|
||||
.coa_compress = nx842_crypto_compress,
|
||||
.coa_decompress = nx842_crypto_decompress } }
|
||||
.coa_compress = crypto842_compress,
|
||||
.coa_decompress = crypto842_decompress } }
|
||||
};
|
||||
|
||||
static int __init nx842_mod_init(void)
|
||||
static int __init crypto842_mod_init(void)
|
||||
{
|
||||
del_timer(&failover_timer);
|
||||
return crypto_register_alg(&alg);
|
||||
}
|
||||
module_init(crypto842_mod_init);
|
||||
|
||||
static void __exit nx842_mod_exit(void)
|
||||
static void __exit crypto842_mod_exit(void)
|
||||
{
|
||||
crypto_unregister_alg(&alg);
|
||||
}
|
||||
|
||||
module_init(nx842_mod_init);
|
||||
module_exit(nx842_mod_exit);
|
||||
module_exit(crypto842_mod_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("842 Compression Algorithm");
|
||||
MODULE_DESCRIPTION("842 Software Compression Algorithm");
|
||||
MODULE_ALIAS_CRYPTO("842");
|
||||
MODULE_ALIAS_CRYPTO("842-generic");
|
||||
MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
|
||||
|
106
crypto/Kconfig
106
crypto/Kconfig
@ -78,6 +78,10 @@ config CRYPTO_RNG2
|
||||
tristate
|
||||
select CRYPTO_ALGAPI2
|
||||
|
||||
config CRYPTO_RNG_DEFAULT
|
||||
tristate
|
||||
select CRYPTO_DRBG_MENU
|
||||
|
||||
config CRYPTO_PCOMP
|
||||
tristate
|
||||
select CRYPTO_PCOMP2
|
||||
@ -87,6 +91,23 @@ config CRYPTO_PCOMP2
|
||||
tristate
|
||||
select CRYPTO_ALGAPI2
|
||||
|
||||
config CRYPTO_AKCIPHER2
|
||||
tristate
|
||||
select CRYPTO_ALGAPI2
|
||||
|
||||
config CRYPTO_AKCIPHER
|
||||
tristate
|
||||
select CRYPTO_AKCIPHER2
|
||||
select CRYPTO_ALGAPI
|
||||
|
||||
config CRYPTO_RSA
|
||||
tristate "RSA algorithm"
|
||||
select CRYPTO_AKCIPHER
|
||||
select MPILIB
|
||||
select ASN1
|
||||
help
|
||||
Generic implementation of the RSA public key algorithm.
|
||||
|
||||
config CRYPTO_MANAGER
|
||||
tristate "Cryptographic algorithm manager"
|
||||
select CRYPTO_MANAGER2
|
||||
@ -100,6 +121,7 @@ config CRYPTO_MANAGER2
|
||||
select CRYPTO_HASH2
|
||||
select CRYPTO_BLKCIPHER2
|
||||
select CRYPTO_PCOMP2
|
||||
select CRYPTO_AKCIPHER2
|
||||
|
||||
config CRYPTO_USER
|
||||
tristate "Userspace cryptographic algorithm configuration"
|
||||
@ -217,15 +239,39 @@ config CRYPTO_GCM
|
||||
Support for Galois/Counter Mode (GCM) and Galois Message
|
||||
Authentication Code (GMAC). Required for IPSec.
|
||||
|
||||
config CRYPTO_CHACHA20POLY1305
|
||||
tristate "ChaCha20-Poly1305 AEAD support"
|
||||
select CRYPTO_CHACHA20
|
||||
select CRYPTO_POLY1305
|
||||
select CRYPTO_AEAD
|
||||
help
|
||||
ChaCha20-Poly1305 AEAD support, RFC7539.
|
||||
|
||||
Support for the AEAD wrapper using the ChaCha20 stream cipher combined
|
||||
with the Poly1305 authenticator. It is defined in RFC7539 for use in
|
||||
IETF protocols.
|
||||
|
||||
config CRYPTO_SEQIV
|
||||
tristate "Sequence Number IV Generator"
|
||||
select CRYPTO_AEAD
|
||||
select CRYPTO_BLKCIPHER
|
||||
select CRYPTO_RNG
|
||||
select CRYPTO_NULL
|
||||
select CRYPTO_RNG_DEFAULT
|
||||
help
|
||||
This IV generator generates an IV based on a sequence number by
|
||||
xoring it with a salt. This algorithm is mainly useful for CTR
|
||||
|
||||
config CRYPTO_ECHAINIV
|
||||
tristate "Encrypted Chain IV Generator"
|
||||
select CRYPTO_AEAD
|
||||
select CRYPTO_NULL
|
||||
select CRYPTO_RNG_DEFAULT
|
||||
default m
|
||||
help
|
||||
This IV generator generates an IV based on the encryption of
|
||||
a sequence number xored with a salt. This is the default
|
||||
algorithm for CBC.
|
||||
|
||||
comment "Block modes"
|
||||
|
||||
config CRYPTO_CBC
|
||||
@ -415,6 +461,15 @@ config CRYPTO_GHASH
|
||||
help
|
||||
GHASH is message digest algorithm for GCM (Galois/Counter Mode).
|
||||
|
||||
config CRYPTO_POLY1305
|
||||
tristate "Poly1305 authenticator algorithm"
|
||||
help
|
||||
Poly1305 authenticator algorithm, RFC7539.
|
||||
|
||||
Poly1305 is an authenticator algorithm designed by Daniel J. Bernstein.
|
||||
It is used for the ChaCha20-Poly1305 AEAD, specified in RFC7539 for use
|
||||
in IETF protocols. This is the portable C implementation of Poly1305.
|
||||
|
||||
config CRYPTO_MD4
|
||||
tristate "MD4 digest algorithm"
|
||||
select CRYPTO_HASH
|
||||
@ -1145,6 +1200,19 @@ config CRYPTO_SALSA20_X86_64
|
||||
The Salsa20 stream cipher algorithm is designed by Daniel J.
|
||||
Bernstein <djb@cr.yp.to>. See <http://cr.yp.to/snuffle.html>
|
||||
|
||||
config CRYPTO_CHACHA20
|
||||
tristate "ChaCha20 cipher algorithm"
|
||||
select CRYPTO_BLKCIPHER
|
||||
help
|
||||
ChaCha20 cipher algorithm, RFC7539.
|
||||
|
||||
ChaCha20 is a 256-bit high-speed stream cipher designed by Daniel J.
|
||||
Bernstein and further specified in RFC7539 for use in IETF protocols.
|
||||
This is the portable C implementation of ChaCha20.
|
||||
|
||||
See also:
|
||||
<http://cr.yp.to/chacha/chacha-20080128.pdf>
|
||||
|
||||
config CRYPTO_SEED
|
||||
tristate "SEED cipher algorithm"
|
||||
select CRYPTO_ALGAPI
|
||||
@ -1412,10 +1480,9 @@ config CRYPTO_LZO
|
||||
|
||||
config CRYPTO_842
|
||||
tristate "842 compression algorithm"
|
||||
depends on CRYPTO_DEV_NX_COMPRESS
|
||||
# 842 uses lzo if the hardware becomes unavailable
|
||||
select LZO_COMPRESS
|
||||
select LZO_DECOMPRESS
|
||||
select CRYPTO_ALGAPI
|
||||
select 842_COMPRESS
|
||||
select 842_DECOMPRESS
|
||||
help
|
||||
This is the 842 algorithm.
|
||||
|
||||
@ -1439,7 +1506,6 @@ comment "Random Number Generation"
|
||||
|
||||
config CRYPTO_ANSI_CPRNG
|
||||
tristate "Pseudo Random Number Generation for Cryptographic modules"
|
||||
default m
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_RNG
|
||||
help
|
||||
@ -1457,15 +1523,14 @@ menuconfig CRYPTO_DRBG_MENU
|
||||
if CRYPTO_DRBG_MENU
|
||||
|
||||
config CRYPTO_DRBG_HMAC
|
||||
bool "Enable HMAC DRBG"
|
||||
bool
|
||||
default y
|
||||
select CRYPTO_HMAC
|
||||
help
|
||||
Enable the HMAC DRBG variant as defined in NIST SP800-90A.
|
||||
select CRYPTO_SHA256
|
||||
|
||||
config CRYPTO_DRBG_HASH
|
||||
bool "Enable Hash DRBG"
|
||||
select CRYPTO_HASH
|
||||
select CRYPTO_SHA256
|
||||
help
|
||||
Enable the Hash DRBG variant as defined in NIST SP800-90A.
|
||||
|
||||
@ -1477,11 +1542,21 @@ config CRYPTO_DRBG_CTR
|
||||
|
||||
config CRYPTO_DRBG
|
||||
tristate
|
||||
default CRYPTO_DRBG_MENU if (CRYPTO_DRBG_HMAC || CRYPTO_DRBG_HASH || CRYPTO_DRBG_CTR)
|
||||
default CRYPTO_DRBG_MENU
|
||||
select CRYPTO_RNG
|
||||
select CRYPTO_JITTERENTROPY
|
||||
|
||||
endif # if CRYPTO_DRBG_MENU
|
||||
|
||||
config CRYPTO_JITTERENTROPY
|
||||
tristate "Jitterentropy Non-Deterministic Random Number Generator"
|
||||
help
|
||||
The Jitterentropy RNG is a noise that is intended
|
||||
to provide seed to another RNG. The RNG does not
|
||||
perform any cryptographic whitening of the generated
|
||||
random numbers. This Jitterentropy RNG registers with
|
||||
the kernel crypto API and can be used by any caller.
|
||||
|
||||
config CRYPTO_USER_API
|
||||
tristate
|
||||
|
||||
@ -1512,6 +1587,15 @@ config CRYPTO_USER_API_RNG
|
||||
This option enables the user-spaces interface for random
|
||||
number generator algorithms.
|
||||
|
||||
config CRYPTO_USER_API_AEAD
|
||||
tristate "User-space interface for AEAD cipher algorithms"
|
||||
depends on NET
|
||||
select CRYPTO_AEAD
|
||||
select CRYPTO_USER_API
|
||||
help
|
||||
This option enables the user-spaces interface for AEAD
|
||||
cipher algorithms.
|
||||
|
||||
config CRYPTO_HASH_INFO
|
||||
bool
|
||||
|
||||
|
@ -21,12 +21,22 @@ obj-$(CONFIG_CRYPTO_BLKCIPHER2) += crypto_blkcipher.o
|
||||
obj-$(CONFIG_CRYPTO_BLKCIPHER2) += chainiv.o
|
||||
obj-$(CONFIG_CRYPTO_BLKCIPHER2) += eseqiv.o
|
||||
obj-$(CONFIG_CRYPTO_SEQIV) += seqiv.o
|
||||
obj-$(CONFIG_CRYPTO_ECHAINIV) += echainiv.o
|
||||
|
||||
crypto_hash-y += ahash.o
|
||||
crypto_hash-y += shash.o
|
||||
obj-$(CONFIG_CRYPTO_HASH2) += crypto_hash.o
|
||||
|
||||
obj-$(CONFIG_CRYPTO_PCOMP2) += pcompress.o
|
||||
obj-$(CONFIG_CRYPTO_AKCIPHER2) += akcipher.o
|
||||
|
||||
$(obj)/rsakey-asn1.o: $(obj)/rsakey-asn1.c $(obj)/rsakey-asn1.h
|
||||
clean-files += rsakey-asn1.c rsakey-asn1.h
|
||||
|
||||
rsa_generic-y := rsakey-asn1.o
|
||||
rsa_generic-y += rsa.o
|
||||
rsa_generic-y += rsa_helper.o
|
||||
obj-$(CONFIG_CRYPTO_RSA) += rsa_generic.o
|
||||
|
||||
cryptomgr-y := algboss.o testmgr.o
|
||||
|
||||
@ -58,6 +68,7 @@ obj-$(CONFIG_CRYPTO_XTS) += xts.o
|
||||
obj-$(CONFIG_CRYPTO_CTR) += ctr.o
|
||||
obj-$(CONFIG_CRYPTO_GCM) += gcm.o
|
||||
obj-$(CONFIG_CRYPTO_CCM) += ccm.o
|
||||
obj-$(CONFIG_CRYPTO_CHACHA20POLY1305) += chacha20poly1305.o
|
||||
obj-$(CONFIG_CRYPTO_PCRYPT) += pcrypt.o
|
||||
obj-$(CONFIG_CRYPTO_CRYPTD) += cryptd.o
|
||||
obj-$(CONFIG_CRYPTO_MCRYPTD) += mcryptd.o
|
||||
@ -79,6 +90,8 @@ obj-$(CONFIG_CRYPTO_KHAZAD) += khazad.o
|
||||
obj-$(CONFIG_CRYPTO_ANUBIS) += anubis.o
|
||||
obj-$(CONFIG_CRYPTO_SEED) += seed.o
|
||||
obj-$(CONFIG_CRYPTO_SALSA20) += salsa20_generic.o
|
||||
obj-$(CONFIG_CRYPTO_CHACHA20) += chacha20_generic.o
|
||||
obj-$(CONFIG_CRYPTO_POLY1305) += poly1305_generic.o
|
||||
obj-$(CONFIG_CRYPTO_DEFLATE) += deflate.o
|
||||
obj-$(CONFIG_CRYPTO_ZLIB) += zlib.o
|
||||
obj-$(CONFIG_CRYPTO_MICHAEL_MIC) += michael_mic.o
|
||||
@ -91,9 +104,9 @@ obj-$(CONFIG_CRYPTO_LZ4) += lz4.o
|
||||
obj-$(CONFIG_CRYPTO_LZ4HC) += lz4hc.o
|
||||
obj-$(CONFIG_CRYPTO_842) += 842.o
|
||||
obj-$(CONFIG_CRYPTO_RNG2) += rng.o
|
||||
obj-$(CONFIG_CRYPTO_RNG2) += krng.o
|
||||
obj-$(CONFIG_CRYPTO_ANSI_CPRNG) += ansi_cprng.o
|
||||
obj-$(CONFIG_CRYPTO_DRBG) += drbg.o
|
||||
obj-$(CONFIG_CRYPTO_JITTERENTROPY) += jitterentropy.o
|
||||
obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o
|
||||
obj-$(CONFIG_CRYPTO_GHASH) += ghash-generic.o
|
||||
obj-$(CONFIG_CRYPTO_USER_API) += af_alg.o
|
||||
|
@ -454,7 +454,7 @@ static int crypto_init_givcipher_ops(struct crypto_tfm *tfm, u32 type,
|
||||
alg->setkey : setkey;
|
||||
crt->encrypt = alg->encrypt;
|
||||
crt->decrypt = alg->decrypt;
|
||||
crt->givencrypt = alg->givencrypt;
|
||||
crt->givencrypt = alg->givencrypt ?: no_givdecrypt;
|
||||
crt->givdecrypt = alg->givdecrypt ?: no_givdecrypt;
|
||||
crt->base = __crypto_ablkcipher_cast(tfm);
|
||||
crt->ivsize = alg->ivsize;
|
||||
@ -586,6 +586,13 @@ static int crypto_givcipher_default(struct crypto_alg *alg, u32 type, u32 mask)
|
||||
if (!tmpl)
|
||||
goto kill_larval;
|
||||
|
||||
if (tmpl->create) {
|
||||
err = tmpl->create(tmpl, tb);
|
||||
if (err)
|
||||
goto put_tmpl;
|
||||
goto ok;
|
||||
}
|
||||
|
||||
inst = tmpl->alloc(tb);
|
||||
err = PTR_ERR(inst);
|
||||
if (IS_ERR(inst))
|
||||
@ -597,6 +604,7 @@ static int crypto_givcipher_default(struct crypto_alg *alg, u32 type, u32 mask)
|
||||
goto put_tmpl;
|
||||
}
|
||||
|
||||
ok:
|
||||
/* Redo the lookup to use the instance we just registered. */
|
||||
err = -EAGAIN;
|
||||
|
||||
@ -636,7 +644,7 @@ struct crypto_alg *crypto_lookup_skcipher(const char *name, u32 type, u32 mask)
|
||||
|
||||
if ((alg->cra_flags & CRYPTO_ALG_TYPE_MASK) ==
|
||||
CRYPTO_ALG_TYPE_GIVCIPHER) {
|
||||
if ((alg->cra_flags ^ type ^ ~mask) & CRYPTO_ALG_TESTED) {
|
||||
if (~alg->cra_flags & (type ^ ~mask) & CRYPTO_ALG_TESTED) {
|
||||
crypto_mod_put(alg);
|
||||
alg = ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
702
crypto/aead.c
702
crypto/aead.c
@ -12,7 +12,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <crypto/internal/aead.h>
|
||||
#include <crypto/internal/geniv.h>
|
||||
#include <crypto/scatterwalk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
@ -26,10 +27,20 @@
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
struct compat_request_ctx {
|
||||
struct scatterlist src[2];
|
||||
struct scatterlist dst[2];
|
||||
struct scatterlist ivbuf[2];
|
||||
struct scatterlist *ivsg;
|
||||
struct aead_givcrypt_request subreq;
|
||||
};
|
||||
|
||||
static int aead_null_givencrypt(struct aead_givcrypt_request *req);
|
||||
static int aead_null_givdecrypt(struct aead_givcrypt_request *req);
|
||||
|
||||
static int setkey_unaligned(struct crypto_aead *tfm, const u8 *key,
|
||||
unsigned int keylen)
|
||||
{
|
||||
struct aead_alg *aead = crypto_aead_alg(tfm);
|
||||
unsigned long alignmask = crypto_aead_alignmask(tfm);
|
||||
int ret;
|
||||
u8 *buffer, *alignbuffer;
|
||||
@ -42,47 +53,95 @@ static int setkey_unaligned(struct crypto_aead *tfm, const u8 *key,
|
||||
|
||||
alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1);
|
||||
memcpy(alignbuffer, key, keylen);
|
||||
ret = aead->setkey(tfm, alignbuffer, keylen);
|
||||
ret = tfm->setkey(tfm, alignbuffer, keylen);
|
||||
memset(alignbuffer, 0, keylen);
|
||||
kfree(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int setkey(struct crypto_aead *tfm, const u8 *key, unsigned int keylen)
|
||||
int crypto_aead_setkey(struct crypto_aead *tfm,
|
||||
const u8 *key, unsigned int keylen)
|
||||
{
|
||||
struct aead_alg *aead = crypto_aead_alg(tfm);
|
||||
unsigned long alignmask = crypto_aead_alignmask(tfm);
|
||||
|
||||
tfm = tfm->child;
|
||||
|
||||
if ((unsigned long)key & alignmask)
|
||||
return setkey_unaligned(tfm, key, keylen);
|
||||
|
||||
return aead->setkey(tfm, key, keylen);
|
||||
return tfm->setkey(tfm, key, keylen);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_aead_setkey);
|
||||
|
||||
int crypto_aead_setauthsize(struct crypto_aead *tfm, unsigned int authsize)
|
||||
{
|
||||
struct aead_tfm *crt = crypto_aead_crt(tfm);
|
||||
int err;
|
||||
|
||||
if (authsize > crypto_aead_alg(tfm)->maxauthsize)
|
||||
if (authsize > crypto_aead_maxauthsize(tfm))
|
||||
return -EINVAL;
|
||||
|
||||
if (crypto_aead_alg(tfm)->setauthsize) {
|
||||
err = crypto_aead_alg(tfm)->setauthsize(crt->base, authsize);
|
||||
if (tfm->setauthsize) {
|
||||
err = tfm->setauthsize(tfm->child, authsize);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
crypto_aead_crt(crt->base)->authsize = authsize;
|
||||
crt->authsize = authsize;
|
||||
tfm->child->authsize = authsize;
|
||||
tfm->authsize = authsize;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_aead_setauthsize);
|
||||
|
||||
static unsigned int crypto_aead_ctxsize(struct crypto_alg *alg, u32 type,
|
||||
u32 mask)
|
||||
struct aead_old_request {
|
||||
struct scatterlist srcbuf[2];
|
||||
struct scatterlist dstbuf[2];
|
||||
struct aead_request subreq;
|
||||
};
|
||||
|
||||
unsigned int crypto_aead_reqsize(struct crypto_aead *tfm)
|
||||
{
|
||||
return alg->cra_ctxsize;
|
||||
return tfm->reqsize + sizeof(struct aead_old_request);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_aead_reqsize);
|
||||
|
||||
static int old_crypt(struct aead_request *req,
|
||||
int (*crypt)(struct aead_request *req))
|
||||
{
|
||||
struct aead_old_request *nreq = aead_request_ctx(req);
|
||||
struct crypto_aead *aead = crypto_aead_reqtfm(req);
|
||||
struct scatterlist *src, *dst;
|
||||
|
||||
if (req->old)
|
||||
return crypt(req);
|
||||
|
||||
src = scatterwalk_ffwd(nreq->srcbuf, req->src, req->assoclen);
|
||||
dst = req->src == req->dst ?
|
||||
src : scatterwalk_ffwd(nreq->dstbuf, req->dst, req->assoclen);
|
||||
|
||||
aead_request_set_tfm(&nreq->subreq, aead);
|
||||
aead_request_set_callback(&nreq->subreq, aead_request_flags(req),
|
||||
req->base.complete, req->base.data);
|
||||
aead_request_set_crypt(&nreq->subreq, src, dst, req->cryptlen,
|
||||
req->iv);
|
||||
aead_request_set_assoc(&nreq->subreq, req->src, req->assoclen);
|
||||
|
||||
return crypt(&nreq->subreq);
|
||||
}
|
||||
|
||||
static int old_encrypt(struct aead_request *req)
|
||||
{
|
||||
struct crypto_aead *aead = crypto_aead_reqtfm(req);
|
||||
struct old_aead_alg *alg = crypto_old_aead_alg(aead);
|
||||
|
||||
return old_crypt(req, alg->encrypt);
|
||||
}
|
||||
|
||||
static int old_decrypt(struct aead_request *req)
|
||||
{
|
||||
struct crypto_aead *aead = crypto_aead_reqtfm(req);
|
||||
struct old_aead_alg *alg = crypto_old_aead_alg(aead);
|
||||
|
||||
return old_crypt(req, alg->decrypt);
|
||||
}
|
||||
|
||||
static int no_givcrypt(struct aead_givcrypt_request *req)
|
||||
@ -90,35 +149,129 @@ static int no_givcrypt(struct aead_givcrypt_request *req)
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int crypto_init_aead_ops(struct crypto_tfm *tfm, u32 type, u32 mask)
|
||||
static int crypto_old_aead_init_tfm(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct aead_alg *alg = &tfm->__crt_alg->cra_aead;
|
||||
struct aead_tfm *crt = &tfm->crt_aead;
|
||||
struct old_aead_alg *alg = &tfm->__crt_alg->cra_aead;
|
||||
struct crypto_aead *crt = __crypto_aead_cast(tfm);
|
||||
|
||||
if (max(alg->maxauthsize, alg->ivsize) > PAGE_SIZE / 8)
|
||||
return -EINVAL;
|
||||
|
||||
crt->setkey = tfm->__crt_alg->cra_flags & CRYPTO_ALG_GENIV ?
|
||||
alg->setkey : setkey;
|
||||
crt->encrypt = alg->encrypt;
|
||||
crt->decrypt = alg->decrypt;
|
||||
crt->givencrypt = alg->givencrypt ?: no_givcrypt;
|
||||
crt->givdecrypt = alg->givdecrypt ?: no_givcrypt;
|
||||
crt->base = __crypto_aead_cast(tfm);
|
||||
crt->ivsize = alg->ivsize;
|
||||
crt->setkey = alg->setkey;
|
||||
crt->setauthsize = alg->setauthsize;
|
||||
crt->encrypt = old_encrypt;
|
||||
crt->decrypt = old_decrypt;
|
||||
if (alg->ivsize) {
|
||||
crt->givencrypt = alg->givencrypt ?: no_givcrypt;
|
||||
crt->givdecrypt = alg->givdecrypt ?: no_givcrypt;
|
||||
} else {
|
||||
crt->givencrypt = aead_null_givencrypt;
|
||||
crt->givdecrypt = aead_null_givdecrypt;
|
||||
}
|
||||
crt->child = __crypto_aead_cast(tfm);
|
||||
crt->authsize = alg->maxauthsize;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void crypto_aead_exit_tfm(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct crypto_aead *aead = __crypto_aead_cast(tfm);
|
||||
struct aead_alg *alg = crypto_aead_alg(aead);
|
||||
|
||||
alg->exit(aead);
|
||||
}
|
||||
|
||||
static int crypto_aead_init_tfm(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct crypto_aead *aead = __crypto_aead_cast(tfm);
|
||||
struct aead_alg *alg = crypto_aead_alg(aead);
|
||||
|
||||
if (crypto_old_aead_alg(aead)->encrypt)
|
||||
return crypto_old_aead_init_tfm(tfm);
|
||||
|
||||
aead->setkey = alg->setkey;
|
||||
aead->setauthsize = alg->setauthsize;
|
||||
aead->encrypt = alg->encrypt;
|
||||
aead->decrypt = alg->decrypt;
|
||||
aead->child = __crypto_aead_cast(tfm);
|
||||
aead->authsize = alg->maxauthsize;
|
||||
|
||||
if (alg->exit)
|
||||
aead->base.exit = crypto_aead_exit_tfm;
|
||||
|
||||
if (alg->init)
|
||||
return alg->init(aead);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET
|
||||
static int crypto_old_aead_report(struct sk_buff *skb, struct crypto_alg *alg)
|
||||
{
|
||||
struct crypto_report_aead raead;
|
||||
struct old_aead_alg *aead = &alg->cra_aead;
|
||||
|
||||
strncpy(raead.type, "aead", sizeof(raead.type));
|
||||
strncpy(raead.geniv, aead->geniv ?: "<built-in>", sizeof(raead.geniv));
|
||||
|
||||
raead.blocksize = alg->cra_blocksize;
|
||||
raead.maxauthsize = aead->maxauthsize;
|
||||
raead.ivsize = aead->ivsize;
|
||||
|
||||
if (nla_put(skb, CRYPTOCFGA_REPORT_AEAD,
|
||||
sizeof(struct crypto_report_aead), &raead))
|
||||
goto nla_put_failure;
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
#else
|
||||
static int crypto_old_aead_report(struct sk_buff *skb, struct crypto_alg *alg)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void crypto_old_aead_show(struct seq_file *m, struct crypto_alg *alg)
|
||||
__attribute__ ((unused));
|
||||
static void crypto_old_aead_show(struct seq_file *m, struct crypto_alg *alg)
|
||||
{
|
||||
struct old_aead_alg *aead = &alg->cra_aead;
|
||||
|
||||
seq_printf(m, "type : aead\n");
|
||||
seq_printf(m, "async : %s\n", alg->cra_flags & CRYPTO_ALG_ASYNC ?
|
||||
"yes" : "no");
|
||||
seq_printf(m, "blocksize : %u\n", alg->cra_blocksize);
|
||||
seq_printf(m, "ivsize : %u\n", aead->ivsize);
|
||||
seq_printf(m, "maxauthsize : %u\n", aead->maxauthsize);
|
||||
seq_printf(m, "geniv : %s\n", aead->geniv ?: "<built-in>");
|
||||
}
|
||||
|
||||
const struct crypto_type crypto_aead_type = {
|
||||
.extsize = crypto_alg_extsize,
|
||||
.init_tfm = crypto_aead_init_tfm,
|
||||
#ifdef CONFIG_PROC_FS
|
||||
.show = crypto_old_aead_show,
|
||||
#endif
|
||||
.report = crypto_old_aead_report,
|
||||
.lookup = crypto_lookup_aead,
|
||||
.maskclear = ~(CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV),
|
||||
.maskset = CRYPTO_ALG_TYPE_MASK,
|
||||
.type = CRYPTO_ALG_TYPE_AEAD,
|
||||
.tfmsize = offsetof(struct crypto_aead, base),
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(crypto_aead_type);
|
||||
|
||||
#ifdef CONFIG_NET
|
||||
static int crypto_aead_report(struct sk_buff *skb, struct crypto_alg *alg)
|
||||
{
|
||||
struct crypto_report_aead raead;
|
||||
struct aead_alg *aead = &alg->cra_aead;
|
||||
struct aead_alg *aead = container_of(alg, struct aead_alg, base);
|
||||
|
||||
strncpy(raead.type, "aead", sizeof(raead.type));
|
||||
strncpy(raead.geniv, aead->geniv ?: "<built-in>", sizeof(raead.geniv));
|
||||
strncpy(raead.geniv, "<none>", sizeof(raead.geniv));
|
||||
|
||||
raead.blocksize = alg->cra_blocksize;
|
||||
raead.maxauthsize = aead->maxauthsize;
|
||||
@ -143,7 +296,7 @@ static void crypto_aead_show(struct seq_file *m, struct crypto_alg *alg)
|
||||
__attribute__ ((unused));
|
||||
static void crypto_aead_show(struct seq_file *m, struct crypto_alg *alg)
|
||||
{
|
||||
struct aead_alg *aead = &alg->cra_aead;
|
||||
struct aead_alg *aead = container_of(alg, struct aead_alg, base);
|
||||
|
||||
seq_printf(m, "type : aead\n");
|
||||
seq_printf(m, "async : %s\n", alg->cra_flags & CRYPTO_ALG_ASYNC ?
|
||||
@ -151,18 +304,21 @@ static void crypto_aead_show(struct seq_file *m, struct crypto_alg *alg)
|
||||
seq_printf(m, "blocksize : %u\n", alg->cra_blocksize);
|
||||
seq_printf(m, "ivsize : %u\n", aead->ivsize);
|
||||
seq_printf(m, "maxauthsize : %u\n", aead->maxauthsize);
|
||||
seq_printf(m, "geniv : %s\n", aead->geniv ?: "<built-in>");
|
||||
seq_printf(m, "geniv : <none>\n");
|
||||
}
|
||||
|
||||
const struct crypto_type crypto_aead_type = {
|
||||
.ctxsize = crypto_aead_ctxsize,
|
||||
.init = crypto_init_aead_ops,
|
||||
static const struct crypto_type crypto_new_aead_type = {
|
||||
.extsize = crypto_alg_extsize,
|
||||
.init_tfm = crypto_aead_init_tfm,
|
||||
#ifdef CONFIG_PROC_FS
|
||||
.show = crypto_aead_show,
|
||||
#endif
|
||||
.report = crypto_aead_report,
|
||||
.maskclear = ~CRYPTO_ALG_TYPE_MASK,
|
||||
.maskset = CRYPTO_ALG_TYPE_MASK,
|
||||
.type = CRYPTO_ALG_TYPE_AEAD,
|
||||
.tfmsize = offsetof(struct crypto_aead, base),
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(crypto_aead_type);
|
||||
|
||||
static int aead_null_givencrypt(struct aead_givcrypt_request *req)
|
||||
{
|
||||
@ -174,33 +330,11 @@ static int aead_null_givdecrypt(struct aead_givcrypt_request *req)
|
||||
return crypto_aead_decrypt(&req->areq);
|
||||
}
|
||||
|
||||
static int crypto_init_nivaead_ops(struct crypto_tfm *tfm, u32 type, u32 mask)
|
||||
{
|
||||
struct aead_alg *alg = &tfm->__crt_alg->cra_aead;
|
||||
struct aead_tfm *crt = &tfm->crt_aead;
|
||||
|
||||
if (max(alg->maxauthsize, alg->ivsize) > PAGE_SIZE / 8)
|
||||
return -EINVAL;
|
||||
|
||||
crt->setkey = setkey;
|
||||
crt->encrypt = alg->encrypt;
|
||||
crt->decrypt = alg->decrypt;
|
||||
if (!alg->ivsize) {
|
||||
crt->givencrypt = aead_null_givencrypt;
|
||||
crt->givdecrypt = aead_null_givdecrypt;
|
||||
}
|
||||
crt->base = __crypto_aead_cast(tfm);
|
||||
crt->ivsize = alg->ivsize;
|
||||
crt->authsize = alg->maxauthsize;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET
|
||||
static int crypto_nivaead_report(struct sk_buff *skb, struct crypto_alg *alg)
|
||||
{
|
||||
struct crypto_report_aead raead;
|
||||
struct aead_alg *aead = &alg->cra_aead;
|
||||
struct old_aead_alg *aead = &alg->cra_aead;
|
||||
|
||||
strncpy(raead.type, "nivaead", sizeof(raead.type));
|
||||
strncpy(raead.geniv, aead->geniv, sizeof(raead.geniv));
|
||||
@ -229,7 +363,7 @@ static void crypto_nivaead_show(struct seq_file *m, struct crypto_alg *alg)
|
||||
__attribute__ ((unused));
|
||||
static void crypto_nivaead_show(struct seq_file *m, struct crypto_alg *alg)
|
||||
{
|
||||
struct aead_alg *aead = &alg->cra_aead;
|
||||
struct old_aead_alg *aead = &alg->cra_aead;
|
||||
|
||||
seq_printf(m, "type : nivaead\n");
|
||||
seq_printf(m, "async : %s\n", alg->cra_flags & CRYPTO_ALG_ASYNC ?
|
||||
@ -241,43 +375,215 @@ static void crypto_nivaead_show(struct seq_file *m, struct crypto_alg *alg)
|
||||
}
|
||||
|
||||
const struct crypto_type crypto_nivaead_type = {
|
||||
.ctxsize = crypto_aead_ctxsize,
|
||||
.init = crypto_init_nivaead_ops,
|
||||
.extsize = crypto_alg_extsize,
|
||||
.init_tfm = crypto_aead_init_tfm,
|
||||
#ifdef CONFIG_PROC_FS
|
||||
.show = crypto_nivaead_show,
|
||||
#endif
|
||||
.report = crypto_nivaead_report,
|
||||
.maskclear = ~(CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV),
|
||||
.maskset = CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV,
|
||||
.type = CRYPTO_ALG_TYPE_AEAD,
|
||||
.tfmsize = offsetof(struct crypto_aead, base),
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(crypto_nivaead_type);
|
||||
|
||||
static int crypto_grab_nivaead(struct crypto_aead_spawn *spawn,
|
||||
const char *name, u32 type, u32 mask)
|
||||
{
|
||||
struct crypto_alg *alg;
|
||||
spawn->base.frontend = &crypto_nivaead_type;
|
||||
return crypto_grab_spawn(&spawn->base, name, type, mask);
|
||||
}
|
||||
|
||||
static int aead_geniv_setkey(struct crypto_aead *tfm,
|
||||
const u8 *key, unsigned int keylen)
|
||||
{
|
||||
struct aead_geniv_ctx *ctx = crypto_aead_ctx(tfm);
|
||||
|
||||
return crypto_aead_setkey(ctx->child, key, keylen);
|
||||
}
|
||||
|
||||
static int aead_geniv_setauthsize(struct crypto_aead *tfm,
|
||||
unsigned int authsize)
|
||||
{
|
||||
struct aead_geniv_ctx *ctx = crypto_aead_ctx(tfm);
|
||||
|
||||
return crypto_aead_setauthsize(ctx->child, authsize);
|
||||
}
|
||||
|
||||
static void compat_encrypt_complete2(struct aead_request *req, int err)
|
||||
{
|
||||
struct compat_request_ctx *rctx = aead_request_ctx(req);
|
||||
struct aead_givcrypt_request *subreq = &rctx->subreq;
|
||||
struct crypto_aead *geniv;
|
||||
|
||||
if (err == -EINPROGRESS)
|
||||
return;
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
geniv = crypto_aead_reqtfm(req);
|
||||
scatterwalk_map_and_copy(subreq->giv, rctx->ivsg, 0,
|
||||
crypto_aead_ivsize(geniv), 1);
|
||||
|
||||
out:
|
||||
kzfree(subreq->giv);
|
||||
}
|
||||
|
||||
static void compat_encrypt_complete(struct crypto_async_request *base, int err)
|
||||
{
|
||||
struct aead_request *req = base->data;
|
||||
|
||||
compat_encrypt_complete2(req, err);
|
||||
aead_request_complete(req, err);
|
||||
}
|
||||
|
||||
static int compat_encrypt(struct aead_request *req)
|
||||
{
|
||||
struct crypto_aead *geniv = crypto_aead_reqtfm(req);
|
||||
struct aead_geniv_ctx *ctx = crypto_aead_ctx(geniv);
|
||||
struct compat_request_ctx *rctx = aead_request_ctx(req);
|
||||
struct aead_givcrypt_request *subreq = &rctx->subreq;
|
||||
unsigned int ivsize = crypto_aead_ivsize(geniv);
|
||||
struct scatterlist *src, *dst;
|
||||
crypto_completion_t compl;
|
||||
void *data;
|
||||
u8 *info;
|
||||
__be64 seq;
|
||||
int err;
|
||||
|
||||
type &= ~(CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV);
|
||||
type |= CRYPTO_ALG_TYPE_AEAD;
|
||||
mask |= CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV;
|
||||
if (req->cryptlen < ivsize)
|
||||
return -EINVAL;
|
||||
|
||||
alg = crypto_alg_mod_lookup(name, type, mask);
|
||||
if (IS_ERR(alg))
|
||||
return PTR_ERR(alg);
|
||||
compl = req->base.complete;
|
||||
data = req->base.data;
|
||||
|
||||
err = crypto_init_spawn(&spawn->base, alg, spawn->base.inst, mask);
|
||||
crypto_mod_put(alg);
|
||||
rctx->ivsg = scatterwalk_ffwd(rctx->ivbuf, req->dst, req->assoclen);
|
||||
info = PageHighMem(sg_page(rctx->ivsg)) ? NULL : sg_virt(rctx->ivsg);
|
||||
|
||||
if (!info) {
|
||||
info = kmalloc(ivsize, req->base.flags &
|
||||
CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL:
|
||||
GFP_ATOMIC);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
compl = compat_encrypt_complete;
|
||||
data = req;
|
||||
}
|
||||
|
||||
memcpy(&seq, req->iv + ivsize - sizeof(seq), sizeof(seq));
|
||||
|
||||
src = scatterwalk_ffwd(rctx->src, req->src, req->assoclen + ivsize);
|
||||
dst = req->src == req->dst ?
|
||||
src : scatterwalk_ffwd(rctx->dst, rctx->ivsg, ivsize);
|
||||
|
||||
aead_givcrypt_set_tfm(subreq, ctx->child);
|
||||
aead_givcrypt_set_callback(subreq, req->base.flags,
|
||||
req->base.complete, req->base.data);
|
||||
aead_givcrypt_set_crypt(subreq, src, dst,
|
||||
req->cryptlen - ivsize, req->iv);
|
||||
aead_givcrypt_set_assoc(subreq, req->src, req->assoclen);
|
||||
aead_givcrypt_set_giv(subreq, info, be64_to_cpu(seq));
|
||||
|
||||
err = crypto_aead_givencrypt(subreq);
|
||||
if (unlikely(PageHighMem(sg_page(rctx->ivsg))))
|
||||
compat_encrypt_complete2(req, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
struct crypto_instance *aead_geniv_alloc(struct crypto_template *tmpl,
|
||||
struct rtattr **tb, u32 type,
|
||||
u32 mask)
|
||||
static int compat_decrypt(struct aead_request *req)
|
||||
{
|
||||
struct crypto_aead *geniv = crypto_aead_reqtfm(req);
|
||||
struct aead_geniv_ctx *ctx = crypto_aead_ctx(geniv);
|
||||
struct compat_request_ctx *rctx = aead_request_ctx(req);
|
||||
struct aead_request *subreq = &rctx->subreq.areq;
|
||||
unsigned int ivsize = crypto_aead_ivsize(geniv);
|
||||
struct scatterlist *src, *dst;
|
||||
crypto_completion_t compl;
|
||||
void *data;
|
||||
|
||||
if (req->cryptlen < ivsize)
|
||||
return -EINVAL;
|
||||
|
||||
aead_request_set_tfm(subreq, ctx->child);
|
||||
|
||||
compl = req->base.complete;
|
||||
data = req->base.data;
|
||||
|
||||
src = scatterwalk_ffwd(rctx->src, req->src, req->assoclen + ivsize);
|
||||
dst = req->src == req->dst ?
|
||||
src : scatterwalk_ffwd(rctx->dst, req->dst,
|
||||
req->assoclen + ivsize);
|
||||
|
||||
aead_request_set_callback(subreq, req->base.flags, compl, data);
|
||||
aead_request_set_crypt(subreq, src, dst,
|
||||
req->cryptlen - ivsize, req->iv);
|
||||
aead_request_set_assoc(subreq, req->src, req->assoclen);
|
||||
|
||||
scatterwalk_map_and_copy(req->iv, req->src, req->assoclen, ivsize, 0);
|
||||
|
||||
return crypto_aead_decrypt(subreq);
|
||||
}
|
||||
|
||||
static int compat_encrypt_first(struct aead_request *req)
|
||||
{
|
||||
struct crypto_aead *geniv = crypto_aead_reqtfm(req);
|
||||
struct aead_geniv_ctx *ctx = crypto_aead_ctx(geniv);
|
||||
int err = 0;
|
||||
|
||||
spin_lock_bh(&ctx->lock);
|
||||
if (geniv->encrypt != compat_encrypt_first)
|
||||
goto unlock;
|
||||
|
||||
geniv->encrypt = compat_encrypt;
|
||||
|
||||
unlock:
|
||||
spin_unlock_bh(&ctx->lock);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return compat_encrypt(req);
|
||||
}
|
||||
|
||||
static int aead_geniv_init_compat(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct crypto_aead *geniv = __crypto_aead_cast(tfm);
|
||||
struct aead_geniv_ctx *ctx = crypto_aead_ctx(geniv);
|
||||
int err;
|
||||
|
||||
spin_lock_init(&ctx->lock);
|
||||
|
||||
crypto_aead_set_reqsize(geniv, sizeof(struct compat_request_ctx));
|
||||
|
||||
err = aead_geniv_init(tfm);
|
||||
|
||||
ctx->child = geniv->child;
|
||||
geniv->child = geniv;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void aead_geniv_exit_compat(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct crypto_aead *geniv = __crypto_aead_cast(tfm);
|
||||
struct aead_geniv_ctx *ctx = crypto_aead_ctx(geniv);
|
||||
|
||||
crypto_free_aead(ctx->child);
|
||||
}
|
||||
|
||||
struct aead_instance *aead_geniv_alloc(struct crypto_template *tmpl,
|
||||
struct rtattr **tb, u32 type, u32 mask)
|
||||
{
|
||||
const char *name;
|
||||
struct crypto_aead_spawn *spawn;
|
||||
struct crypto_attr_type *algt;
|
||||
struct crypto_instance *inst;
|
||||
struct crypto_alg *alg;
|
||||
struct aead_instance *inst;
|
||||
struct aead_alg *alg;
|
||||
unsigned int ivsize;
|
||||
unsigned int maxauthsize;
|
||||
int err;
|
||||
|
||||
algt = crypto_get_attr_type(tb);
|
||||
@ -296,20 +602,25 @@ struct crypto_instance *aead_geniv_alloc(struct crypto_template *tmpl,
|
||||
if (!inst)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
spawn = crypto_instance_ctx(inst);
|
||||
spawn = aead_instance_ctx(inst);
|
||||
|
||||
/* Ignore async algorithms if necessary. */
|
||||
mask |= crypto_requires_sync(algt->type, algt->mask);
|
||||
|
||||
crypto_set_aead_spawn(spawn, inst);
|
||||
err = crypto_grab_nivaead(spawn, name, type, mask);
|
||||
crypto_set_aead_spawn(spawn, aead_crypto_instance(inst));
|
||||
err = (algt->mask & CRYPTO_ALG_GENIV) ?
|
||||
crypto_grab_nivaead(spawn, name, type, mask) :
|
||||
crypto_grab_aead(spawn, name, type, mask);
|
||||
if (err)
|
||||
goto err_free_inst;
|
||||
|
||||
alg = crypto_aead_spawn_alg(spawn);
|
||||
alg = crypto_spawn_aead_alg(spawn);
|
||||
|
||||
ivsize = crypto_aead_alg_ivsize(alg);
|
||||
maxauthsize = crypto_aead_alg_maxauthsize(alg);
|
||||
|
||||
err = -EINVAL;
|
||||
if (!alg->cra_aead.ivsize)
|
||||
if (ivsize < sizeof(u64))
|
||||
goto err_drop_alg;
|
||||
|
||||
/*
|
||||
@ -318,39 +629,64 @@ struct crypto_instance *aead_geniv_alloc(struct crypto_template *tmpl,
|
||||
* template name and double-check the IV generator.
|
||||
*/
|
||||
if (algt->mask & CRYPTO_ALG_GENIV) {
|
||||
if (strcmp(tmpl->name, alg->cra_aead.geniv))
|
||||
if (!alg->base.cra_aead.encrypt)
|
||||
goto err_drop_alg;
|
||||
if (strcmp(tmpl->name, alg->base.cra_aead.geniv))
|
||||
goto err_drop_alg;
|
||||
|
||||
memcpy(inst->alg.cra_name, alg->cra_name, CRYPTO_MAX_ALG_NAME);
|
||||
memcpy(inst->alg.cra_driver_name, alg->cra_driver_name,
|
||||
memcpy(inst->alg.base.cra_name, alg->base.cra_name,
|
||||
CRYPTO_MAX_ALG_NAME);
|
||||
} else {
|
||||
err = -ENAMETOOLONG;
|
||||
if (snprintf(inst->alg.cra_name, CRYPTO_MAX_ALG_NAME,
|
||||
"%s(%s)", tmpl->name, alg->cra_name) >=
|
||||
CRYPTO_MAX_ALG_NAME)
|
||||
goto err_drop_alg;
|
||||
if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME,
|
||||
"%s(%s)", tmpl->name, alg->cra_driver_name) >=
|
||||
CRYPTO_MAX_ALG_NAME)
|
||||
goto err_drop_alg;
|
||||
memcpy(inst->alg.base.cra_driver_name,
|
||||
alg->base.cra_driver_name, CRYPTO_MAX_ALG_NAME);
|
||||
|
||||
inst->alg.base.cra_flags = CRYPTO_ALG_TYPE_AEAD |
|
||||
CRYPTO_ALG_GENIV;
|
||||
inst->alg.base.cra_flags |= alg->base.cra_flags &
|
||||
CRYPTO_ALG_ASYNC;
|
||||
inst->alg.base.cra_priority = alg->base.cra_priority;
|
||||
inst->alg.base.cra_blocksize = alg->base.cra_blocksize;
|
||||
inst->alg.base.cra_alignmask = alg->base.cra_alignmask;
|
||||
inst->alg.base.cra_type = &crypto_aead_type;
|
||||
|
||||
inst->alg.base.cra_aead.ivsize = ivsize;
|
||||
inst->alg.base.cra_aead.maxauthsize = maxauthsize;
|
||||
|
||||
inst->alg.base.cra_aead.setkey = alg->base.cra_aead.setkey;
|
||||
inst->alg.base.cra_aead.setauthsize =
|
||||
alg->base.cra_aead.setauthsize;
|
||||
inst->alg.base.cra_aead.encrypt = alg->base.cra_aead.encrypt;
|
||||
inst->alg.base.cra_aead.decrypt = alg->base.cra_aead.decrypt;
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
inst->alg.cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_GENIV;
|
||||
inst->alg.cra_flags |= alg->cra_flags & CRYPTO_ALG_ASYNC;
|
||||
inst->alg.cra_priority = alg->cra_priority;
|
||||
inst->alg.cra_blocksize = alg->cra_blocksize;
|
||||
inst->alg.cra_alignmask = alg->cra_alignmask;
|
||||
inst->alg.cra_type = &crypto_aead_type;
|
||||
err = -ENAMETOOLONG;
|
||||
if (snprintf(inst->alg.base.cra_name, CRYPTO_MAX_ALG_NAME,
|
||||
"%s(%s)", tmpl->name, alg->base.cra_name) >=
|
||||
CRYPTO_MAX_ALG_NAME)
|
||||
goto err_drop_alg;
|
||||
if (snprintf(inst->alg.base.cra_driver_name, CRYPTO_MAX_ALG_NAME,
|
||||
"%s(%s)", tmpl->name, alg->base.cra_driver_name) >=
|
||||
CRYPTO_MAX_ALG_NAME)
|
||||
goto err_drop_alg;
|
||||
|
||||
inst->alg.cra_aead.ivsize = alg->cra_aead.ivsize;
|
||||
inst->alg.cra_aead.maxauthsize = alg->cra_aead.maxauthsize;
|
||||
inst->alg.cra_aead.geniv = alg->cra_aead.geniv;
|
||||
inst->alg.base.cra_flags = alg->base.cra_flags & CRYPTO_ALG_ASYNC;
|
||||
inst->alg.base.cra_priority = alg->base.cra_priority;
|
||||
inst->alg.base.cra_blocksize = alg->base.cra_blocksize;
|
||||
inst->alg.base.cra_alignmask = alg->base.cra_alignmask;
|
||||
inst->alg.base.cra_ctxsize = sizeof(struct aead_geniv_ctx);
|
||||
|
||||
inst->alg.cra_aead.setkey = alg->cra_aead.setkey;
|
||||
inst->alg.cra_aead.setauthsize = alg->cra_aead.setauthsize;
|
||||
inst->alg.cra_aead.encrypt = alg->cra_aead.encrypt;
|
||||
inst->alg.cra_aead.decrypt = alg->cra_aead.decrypt;
|
||||
inst->alg.setkey = aead_geniv_setkey;
|
||||
inst->alg.setauthsize = aead_geniv_setauthsize;
|
||||
|
||||
inst->alg.ivsize = ivsize;
|
||||
inst->alg.maxauthsize = maxauthsize;
|
||||
|
||||
inst->alg.encrypt = compat_encrypt_first;
|
||||
inst->alg.decrypt = compat_decrypt;
|
||||
|
||||
inst->alg.base.cra_init = aead_geniv_init_compat;
|
||||
inst->alg.base.cra_exit = aead_geniv_exit_compat;
|
||||
|
||||
out:
|
||||
return inst;
|
||||
@ -364,9 +700,9 @@ err_free_inst:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(aead_geniv_alloc);
|
||||
|
||||
void aead_geniv_free(struct crypto_instance *inst)
|
||||
void aead_geniv_free(struct aead_instance *inst)
|
||||
{
|
||||
crypto_drop_aead(crypto_instance_ctx(inst));
|
||||
crypto_drop_aead(aead_instance_ctx(inst));
|
||||
kfree(inst);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(aead_geniv_free);
|
||||
@ -374,14 +710,17 @@ EXPORT_SYMBOL_GPL(aead_geniv_free);
|
||||
int aead_geniv_init(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct crypto_instance *inst = (void *)tfm->__crt_alg;
|
||||
struct crypto_aead *child;
|
||||
struct crypto_aead *aead;
|
||||
|
||||
aead = crypto_spawn_aead(crypto_instance_ctx(inst));
|
||||
if (IS_ERR(aead))
|
||||
return PTR_ERR(aead);
|
||||
aead = __crypto_aead_cast(tfm);
|
||||
|
||||
tfm->crt_aead.base = aead;
|
||||
tfm->crt_aead.reqsize += crypto_aead_reqsize(aead);
|
||||
child = crypto_spawn_aead(crypto_instance_ctx(inst));
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
|
||||
aead->child = child;
|
||||
aead->reqsize += crypto_aead_reqsize(child);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -389,7 +728,7 @@ EXPORT_SYMBOL_GPL(aead_geniv_init);
|
||||
|
||||
void aead_geniv_exit(struct crypto_tfm *tfm)
|
||||
{
|
||||
crypto_free_aead(tfm->crt_aead.base);
|
||||
crypto_free_aead(__crypto_aead_cast(tfm)->child);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(aead_geniv_exit);
|
||||
|
||||
@ -443,6 +782,13 @@ static int crypto_nivaead_default(struct crypto_alg *alg, u32 type, u32 mask)
|
||||
if (!tmpl)
|
||||
goto kill_larval;
|
||||
|
||||
if (tmpl->create) {
|
||||
err = tmpl->create(tmpl, tb);
|
||||
if (err)
|
||||
goto put_tmpl;
|
||||
goto ok;
|
||||
}
|
||||
|
||||
inst = tmpl->alloc(tb);
|
||||
err = PTR_ERR(inst);
|
||||
if (IS_ERR(inst))
|
||||
@ -454,6 +800,7 @@ static int crypto_nivaead_default(struct crypto_alg *alg, u32 type, u32 mask)
|
||||
goto put_tmpl;
|
||||
}
|
||||
|
||||
ok:
|
||||
/* Redo the lookup to use the instance we just registered. */
|
||||
err = -EAGAIN;
|
||||
|
||||
@ -489,7 +836,7 @@ struct crypto_alg *crypto_lookup_aead(const char *name, u32 type, u32 mask)
|
||||
return alg;
|
||||
|
||||
if (alg->cra_type == &crypto_aead_type) {
|
||||
if ((alg->cra_flags ^ type ^ ~mask) & CRYPTO_ALG_TESTED) {
|
||||
if (~alg->cra_flags & (type ^ ~mask) & CRYPTO_ALG_TESTED) {
|
||||
crypto_mod_put(alg);
|
||||
alg = ERR_PTR(-ENOENT);
|
||||
}
|
||||
@ -505,62 +852,91 @@ EXPORT_SYMBOL_GPL(crypto_lookup_aead);
|
||||
int crypto_grab_aead(struct crypto_aead_spawn *spawn, const char *name,
|
||||
u32 type, u32 mask)
|
||||
{
|
||||
struct crypto_alg *alg;
|
||||
int err;
|
||||
|
||||
type &= ~(CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV);
|
||||
type |= CRYPTO_ALG_TYPE_AEAD;
|
||||
mask &= ~(CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV);
|
||||
mask |= CRYPTO_ALG_TYPE_MASK;
|
||||
|
||||
alg = crypto_lookup_aead(name, type, mask);
|
||||
if (IS_ERR(alg))
|
||||
return PTR_ERR(alg);
|
||||
|
||||
err = crypto_init_spawn(&spawn->base, alg, spawn->base.inst, mask);
|
||||
crypto_mod_put(alg);
|
||||
return err;
|
||||
spawn->base.frontend = &crypto_aead_type;
|
||||
return crypto_grab_spawn(&spawn->base, name, type, mask);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_grab_aead);
|
||||
|
||||
struct crypto_aead *crypto_alloc_aead(const char *alg_name, u32 type, u32 mask)
|
||||
{
|
||||
struct crypto_tfm *tfm;
|
||||
int err;
|
||||
|
||||
type &= ~(CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV);
|
||||
type |= CRYPTO_ALG_TYPE_AEAD;
|
||||
mask &= ~(CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV);
|
||||
mask |= CRYPTO_ALG_TYPE_MASK;
|
||||
|
||||
for (;;) {
|
||||
struct crypto_alg *alg;
|
||||
|
||||
alg = crypto_lookup_aead(alg_name, type, mask);
|
||||
if (IS_ERR(alg)) {
|
||||
err = PTR_ERR(alg);
|
||||
goto err;
|
||||
}
|
||||
|
||||
tfm = __crypto_alloc_tfm(alg, type, mask);
|
||||
if (!IS_ERR(tfm))
|
||||
return __crypto_aead_cast(tfm);
|
||||
|
||||
crypto_mod_put(alg);
|
||||
err = PTR_ERR(tfm);
|
||||
|
||||
err:
|
||||
if (err != -EAGAIN)
|
||||
break;
|
||||
if (signal_pending(current)) {
|
||||
err = -EINTR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ERR_PTR(err);
|
||||
return crypto_alloc_tfm(alg_name, &crypto_aead_type, type, mask);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_alloc_aead);
|
||||
|
||||
static int aead_prepare_alg(struct aead_alg *alg)
|
||||
{
|
||||
struct crypto_alg *base = &alg->base;
|
||||
|
||||
if (max(alg->maxauthsize, alg->ivsize) > PAGE_SIZE / 8)
|
||||
return -EINVAL;
|
||||
|
||||
base->cra_type = &crypto_new_aead_type;
|
||||
base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK;
|
||||
base->cra_flags |= CRYPTO_ALG_TYPE_AEAD;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int crypto_register_aead(struct aead_alg *alg)
|
||||
{
|
||||
struct crypto_alg *base = &alg->base;
|
||||
int err;
|
||||
|
||||
err = aead_prepare_alg(alg);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return crypto_register_alg(base);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_register_aead);
|
||||
|
||||
void crypto_unregister_aead(struct aead_alg *alg)
|
||||
{
|
||||
crypto_unregister_alg(&alg->base);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_unregister_aead);
|
||||
|
||||
int crypto_register_aeads(struct aead_alg *algs, int count)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
ret = crypto_register_aead(&algs[i]);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
for (--i; i >= 0; --i)
|
||||
crypto_unregister_aead(&algs[i]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_register_aeads);
|
||||
|
||||
void crypto_unregister_aeads(struct aead_alg *algs, int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = count - 1; i >= 0; --i)
|
||||
crypto_unregister_aead(&algs[i]);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_unregister_aeads);
|
||||
|
||||
int aead_register_instance(struct crypto_template *tmpl,
|
||||
struct aead_instance *inst)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = aead_prepare_alg(&inst->alg);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return crypto_register_instance(tmpl, aead_crypto_instance(inst));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(aead_register_instance);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Authenticated Encryption with Associated Data (AEAD)");
|
||||
|
@ -127,6 +127,7 @@ EXPORT_SYMBOL_GPL(af_alg_release);
|
||||
|
||||
static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
|
||||
{
|
||||
const u32 forbidden = CRYPTO_ALG_INTERNAL;
|
||||
struct sock *sk = sock->sk;
|
||||
struct alg_sock *ask = alg_sk(sk);
|
||||
struct sockaddr_alg *sa = (void *)uaddr;
|
||||
@ -151,7 +152,9 @@ static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
|
||||
if (IS_ERR(type))
|
||||
return PTR_ERR(type);
|
||||
|
||||
private = type->bind(sa->salg_name, sa->salg_feat, sa->salg_mask);
|
||||
private = type->bind(sa->salg_name,
|
||||
sa->salg_feat & ~forbidden,
|
||||
sa->salg_mask & ~forbidden);
|
||||
if (IS_ERR(private)) {
|
||||
module_put(type->owner);
|
||||
return PTR_ERR(private);
|
||||
|
117
crypto/akcipher.c
Normal file
117
crypto/akcipher.c
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Public Key Encryption
|
||||
*
|
||||
* Copyright (c) 2015, Intel Corporation
|
||||
* Authors: Tadeusz Struk <tadeusz.struk@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
*/
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <crypto/algapi.h>
|
||||
#include <linux/cryptouser.h>
|
||||
#include <net/netlink.h>
|
||||
#include <crypto/akcipher.h>
|
||||
#include <crypto/public_key.h>
|
||||
#include "internal.h"
|
||||
|
||||
#ifdef CONFIG_NET
|
||||
static int crypto_akcipher_report(struct sk_buff *skb, struct crypto_alg *alg)
|
||||
{
|
||||
struct crypto_report_akcipher rakcipher;
|
||||
|
||||
strncpy(rakcipher.type, "akcipher", sizeof(rakcipher.type));
|
||||
|
||||
if (nla_put(skb, CRYPTOCFGA_REPORT_AKCIPHER,
|
||||
sizeof(struct crypto_report_akcipher), &rakcipher))
|
||||
goto nla_put_failure;
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
#else
|
||||
static int crypto_akcipher_report(struct sk_buff *skb, struct crypto_alg *alg)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void crypto_akcipher_show(struct seq_file *m, struct crypto_alg *alg)
|
||||
__attribute__ ((unused));
|
||||
|
||||
static void crypto_akcipher_show(struct seq_file *m, struct crypto_alg *alg)
|
||||
{
|
||||
seq_puts(m, "type : akcipher\n");
|
||||
}
|
||||
|
||||
static void crypto_akcipher_exit_tfm(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct crypto_akcipher *akcipher = __crypto_akcipher_tfm(tfm);
|
||||
struct akcipher_alg *alg = crypto_akcipher_alg(akcipher);
|
||||
|
||||
alg->exit(akcipher);
|
||||
}
|
||||
|
||||
static int crypto_akcipher_init_tfm(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct crypto_akcipher *akcipher = __crypto_akcipher_tfm(tfm);
|
||||
struct akcipher_alg *alg = crypto_akcipher_alg(akcipher);
|
||||
|
||||
if (alg->exit)
|
||||
akcipher->base.exit = crypto_akcipher_exit_tfm;
|
||||
|
||||
if (alg->init)
|
||||
return alg->init(akcipher);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct crypto_type crypto_akcipher_type = {
|
||||
.extsize = crypto_alg_extsize,
|
||||
.init_tfm = crypto_akcipher_init_tfm,
|
||||
#ifdef CONFIG_PROC_FS
|
||||
.show = crypto_akcipher_show,
|
||||
#endif
|
||||
.report = crypto_akcipher_report,
|
||||
.maskclear = ~CRYPTO_ALG_TYPE_MASK,
|
||||
.maskset = CRYPTO_ALG_TYPE_MASK,
|
||||
.type = CRYPTO_ALG_TYPE_AKCIPHER,
|
||||
.tfmsize = offsetof(struct crypto_akcipher, base),
|
||||
};
|
||||
|
||||
struct crypto_akcipher *crypto_alloc_akcipher(const char *alg_name, u32 type,
|
||||
u32 mask)
|
||||
{
|
||||
return crypto_alloc_tfm(alg_name, &crypto_akcipher_type, type, mask);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_alloc_akcipher);
|
||||
|
||||
int crypto_register_akcipher(struct akcipher_alg *alg)
|
||||
{
|
||||
struct crypto_alg *base = &alg->base;
|
||||
|
||||
base->cra_type = &crypto_akcipher_type;
|
||||
base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK;
|
||||
base->cra_flags |= CRYPTO_ALG_TYPE_AKCIPHER;
|
||||
return crypto_register_alg(base);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_register_akcipher);
|
||||
|
||||
void crypto_unregister_akcipher(struct akcipher_alg *alg)
|
||||
{
|
||||
crypto_unregister_alg(&alg->base);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_unregister_akcipher);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Generic public key cihper type");
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fips.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
@ -43,12 +44,9 @@ static inline int crypto_set_driver_name(struct crypto_alg *alg)
|
||||
|
||||
static inline void crypto_check_module_sig(struct module *mod)
|
||||
{
|
||||
#ifdef CONFIG_CRYPTO_FIPS
|
||||
if (fips_enabled && mod && !mod->sig_ok)
|
||||
if (fips_enabled && mod && !module_sig_ok(mod))
|
||||
panic("Module %s signature verification failed in FIPS mode\n",
|
||||
mod->name);
|
||||
#endif
|
||||
return;
|
||||
module_name(mod));
|
||||
}
|
||||
|
||||
static int crypto_check_alg(struct crypto_alg *alg)
|
||||
@ -614,6 +612,22 @@ out:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_init_spawn2);
|
||||
|
||||
int crypto_grab_spawn(struct crypto_spawn *spawn, const char *name,
|
||||
u32 type, u32 mask)
|
||||
{
|
||||
struct crypto_alg *alg;
|
||||
int err;
|
||||
|
||||
alg = crypto_find_alg(name, spawn->frontend, type, mask);
|
||||
if (IS_ERR(alg))
|
||||
return PTR_ERR(alg);
|
||||
|
||||
err = crypto_init_spawn(spawn, alg, spawn->inst, mask);
|
||||
crypto_mod_put(alg);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_grab_spawn);
|
||||
|
||||
void crypto_drop_spawn(struct crypto_spawn *spawn)
|
||||
{
|
||||
if (!spawn->alg)
|
||||
@ -964,6 +978,13 @@ void crypto_xor(u8 *dst, const u8 *src, unsigned int size)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_xor);
|
||||
|
||||
unsigned int crypto_alg_extsize(struct crypto_alg *alg)
|
||||
{
|
||||
return alg->cra_ctxsize +
|
||||
(alg->cra_alignmask & ~(crypto_tfm_ctx_alignment() - 1));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_alg_extsize);
|
||||
|
||||
static int __init crypto_algapi_init(void)
|
||||
{
|
||||
crypto_init_proc();
|
||||
|
@ -13,6 +13,7 @@
|
||||
* any later version.
|
||||
*/
|
||||
|
||||
#include <crypto/aead.h>
|
||||
#include <crypto/scatterwalk.h>
|
||||
#include <crypto/if_alg.h>
|
||||
#include <linux/init.h>
|
||||
@ -71,7 +72,7 @@ static inline bool aead_sufficient_data(struct aead_ctx *ctx)
|
||||
{
|
||||
unsigned as = crypto_aead_authsize(crypto_aead_reqtfm(&ctx->aead_req));
|
||||
|
||||
return (ctx->used >= (ctx->aead_assoclen + (ctx->enc ? 0 : as)));
|
||||
return ctx->used >= ctx->aead_assoclen + as;
|
||||
}
|
||||
|
||||
static void aead_put_sgl(struct sock *sk)
|
||||
@ -352,12 +353,8 @@ static int aead_recvmsg(struct socket *sock, struct msghdr *msg, size_t ignored,
|
||||
struct sock *sk = sock->sk;
|
||||
struct alg_sock *ask = alg_sk(sk);
|
||||
struct aead_ctx *ctx = ask->private;
|
||||
unsigned bs = crypto_aead_blocksize(crypto_aead_reqtfm(&ctx->aead_req));
|
||||
unsigned as = crypto_aead_authsize(crypto_aead_reqtfm(&ctx->aead_req));
|
||||
struct aead_sg_list *sgl = &ctx->tsgl;
|
||||
struct scatterlist *sg = NULL;
|
||||
struct scatterlist assoc[ALG_MAX_PAGES];
|
||||
size_t assoclen = 0;
|
||||
unsigned int i = 0;
|
||||
int err = -EINVAL;
|
||||
unsigned long used = 0;
|
||||
@ -406,23 +403,13 @@ static int aead_recvmsg(struct socket *sock, struct msghdr *msg, size_t ignored,
|
||||
if (!aead_sufficient_data(ctx))
|
||||
goto unlock;
|
||||
|
||||
outlen = used;
|
||||
|
||||
/*
|
||||
* The cipher operation input data is reduced by the associated data
|
||||
* length as this data is processed separately later on.
|
||||
*/
|
||||
used -= ctx->aead_assoclen;
|
||||
|
||||
if (ctx->enc) {
|
||||
/* round up output buffer to multiple of block size */
|
||||
outlen = ((used + bs - 1) / bs * bs);
|
||||
/* add the size needed for the auth tag to be created */
|
||||
outlen += as;
|
||||
} else {
|
||||
/* output data size is input without the authentication tag */
|
||||
outlen = used - as;
|
||||
/* round up output buffer to multiple of block size */
|
||||
outlen = ((outlen + bs - 1) / bs * bs);
|
||||
}
|
||||
used -= ctx->aead_assoclen + (ctx->enc ? as : 0);
|
||||
|
||||
/* convert iovecs of output buffers into scatterlists */
|
||||
while (iov_iter_count(&msg->msg_iter)) {
|
||||
@ -451,47 +438,11 @@ static int aead_recvmsg(struct socket *sock, struct msghdr *msg, size_t ignored,
|
||||
if (usedpages < outlen)
|
||||
goto unlock;
|
||||
|
||||
sg_init_table(assoc, ALG_MAX_PAGES);
|
||||
assoclen = ctx->aead_assoclen;
|
||||
/*
|
||||
* Split scatterlist into two: first part becomes AD, second part
|
||||
* is plaintext / ciphertext. The first part is assigned to assoc
|
||||
* scatterlist. When this loop finishes, sg points to the start of the
|
||||
* plaintext / ciphertext.
|
||||
*/
|
||||
for (i = 0; i < ctx->tsgl.cur; i++) {
|
||||
sg = sgl->sg + i;
|
||||
if (sg->length <= assoclen) {
|
||||
/* AD is larger than one page */
|
||||
sg_set_page(assoc + i, sg_page(sg),
|
||||
sg->length, sg->offset);
|
||||
assoclen -= sg->length;
|
||||
if (i >= ctx->tsgl.cur)
|
||||
goto unlock;
|
||||
} else if (!assoclen) {
|
||||
/* current page is to start of plaintext / ciphertext */
|
||||
if (i)
|
||||
/* AD terminates at page boundary */
|
||||
sg_mark_end(assoc + i - 1);
|
||||
else
|
||||
/* AD size is zero */
|
||||
sg_mark_end(assoc);
|
||||
break;
|
||||
} else {
|
||||
/* AD does not terminate at page boundary */
|
||||
sg_set_page(assoc + i, sg_page(sg),
|
||||
assoclen, sg->offset);
|
||||
sg_mark_end(assoc + i);
|
||||
/* plaintext / ciphertext starts after AD */
|
||||
sg->length -= assoclen;
|
||||
sg->offset += assoclen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sg_mark_end(sgl->sg + sgl->cur - 1);
|
||||
|
||||
aead_request_set_assoc(&ctx->aead_req, assoc, ctx->aead_assoclen);
|
||||
aead_request_set_crypt(&ctx->aead_req, sg, ctx->rsgl[0].sg, used,
|
||||
ctx->iv);
|
||||
aead_request_set_crypt(&ctx->aead_req, sgl->sg, ctx->rsgl[0].sg,
|
||||
used, ctx->iv);
|
||||
aead_request_set_ad(&ctx->aead_req, ctx->aead_assoclen);
|
||||
|
||||
err = af_alg_wait_for_completion(ctx->enc ?
|
||||
crypto_aead_encrypt(&ctx->aead_req) :
|
||||
@ -563,7 +514,8 @@ static struct proto_ops algif_aead_ops = {
|
||||
|
||||
static void *aead_bind(const char *name, u32 type, u32 mask)
|
||||
{
|
||||
return crypto_alloc_aead(name, type, mask);
|
||||
return crypto_alloc_aead(name, type | CRYPTO_ALG_AEAD_NEW,
|
||||
mask | CRYPTO_ALG_AEAD_NEW);
|
||||
}
|
||||
|
||||
static void aead_release(void *private)
|
||||
|
@ -164,7 +164,7 @@ static int rng_setkey(void *private, const u8 *seed, unsigned int seedlen)
|
||||
* Check whether seedlen is of sufficient size is done in RNG
|
||||
* implementations.
|
||||
*/
|
||||
return crypto_rng_reset(private, (u8 *)seed, seedlen);
|
||||
return crypto_rng_reset(private, seed, seedlen);
|
||||
}
|
||||
|
||||
static const struct af_alg_type algif_type_rng = {
|
||||
|
@ -20,8 +20,6 @@
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#define DEFAULT_PRNG_KEY "0123456789abcdef"
|
||||
#define DEFAULT_PRNG_KSZ 16
|
||||
#define DEFAULT_BLK_SZ 16
|
||||
@ -281,11 +279,11 @@ static void free_prng_context(struct prng_context *ctx)
|
||||
}
|
||||
|
||||
static int reset_prng_context(struct prng_context *ctx,
|
||||
unsigned char *key, size_t klen,
|
||||
unsigned char *V, unsigned char *DT)
|
||||
const unsigned char *key, size_t klen,
|
||||
const unsigned char *V, const unsigned char *DT)
|
||||
{
|
||||
int ret;
|
||||
unsigned char *prng_key;
|
||||
const unsigned char *prng_key;
|
||||
|
||||
spin_lock_bh(&ctx->prng_lock);
|
||||
ctx->flags |= PRNG_NEED_RESET;
|
||||
@ -353,8 +351,9 @@ static void cprng_exit(struct crypto_tfm *tfm)
|
||||
free_prng_context(crypto_tfm_ctx(tfm));
|
||||
}
|
||||
|
||||
static int cprng_get_random(struct crypto_rng *tfm, u8 *rdata,
|
||||
unsigned int dlen)
|
||||
static int cprng_get_random(struct crypto_rng *tfm,
|
||||
const u8 *src, unsigned int slen,
|
||||
u8 *rdata, unsigned int dlen)
|
||||
{
|
||||
struct prng_context *prng = crypto_rng_ctx(tfm);
|
||||
|
||||
@ -367,11 +366,12 @@ static int cprng_get_random(struct crypto_rng *tfm, u8 *rdata,
|
||||
* V and KEY are required during reset, and DT is optional, detected
|
||||
* as being present by testing the length of the seed
|
||||
*/
|
||||
static int cprng_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen)
|
||||
static int cprng_reset(struct crypto_rng *tfm,
|
||||
const u8 *seed, unsigned int slen)
|
||||
{
|
||||
struct prng_context *prng = crypto_rng_ctx(tfm);
|
||||
u8 *key = seed + DEFAULT_BLK_SZ;
|
||||
u8 *dt = NULL;
|
||||
const u8 *key = seed + DEFAULT_BLK_SZ;
|
||||
const u8 *dt = NULL;
|
||||
|
||||
if (slen < DEFAULT_PRNG_KSZ + DEFAULT_BLK_SZ)
|
||||
return -EINVAL;
|
||||
@ -387,18 +387,20 @@ static int cprng_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CRYPTO_FIPS
|
||||
static int fips_cprng_get_random(struct crypto_rng *tfm, u8 *rdata,
|
||||
unsigned int dlen)
|
||||
static int fips_cprng_get_random(struct crypto_rng *tfm,
|
||||
const u8 *src, unsigned int slen,
|
||||
u8 *rdata, unsigned int dlen)
|
||||
{
|
||||
struct prng_context *prng = crypto_rng_ctx(tfm);
|
||||
|
||||
return get_prng_bytes(rdata, dlen, prng, 1);
|
||||
}
|
||||
|
||||
static int fips_cprng_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen)
|
||||
static int fips_cprng_reset(struct crypto_rng *tfm,
|
||||
const u8 *seed, unsigned int slen)
|
||||
{
|
||||
u8 rdata[DEFAULT_BLK_SZ];
|
||||
u8 *key = seed + DEFAULT_BLK_SZ;
|
||||
const u8 *key = seed + DEFAULT_BLK_SZ;
|
||||
int rc;
|
||||
|
||||
struct prng_context *prng = crypto_rng_ctx(tfm);
|
||||
@ -424,40 +426,32 @@ out:
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct crypto_alg rng_algs[] = { {
|
||||
.cra_name = "stdrng",
|
||||
.cra_driver_name = "ansi_cprng",
|
||||
.cra_priority = 100,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_RNG,
|
||||
.cra_ctxsize = sizeof(struct prng_context),
|
||||
.cra_type = &crypto_rng_type,
|
||||
.cra_module = THIS_MODULE,
|
||||
.cra_init = cprng_init,
|
||||
.cra_exit = cprng_exit,
|
||||
.cra_u = {
|
||||
.rng = {
|
||||
.rng_make_random = cprng_get_random,
|
||||
.rng_reset = cprng_reset,
|
||||
.seedsize = DEFAULT_PRNG_KSZ + 2*DEFAULT_BLK_SZ,
|
||||
}
|
||||
static struct rng_alg rng_algs[] = { {
|
||||
.generate = cprng_get_random,
|
||||
.seed = cprng_reset,
|
||||
.seedsize = DEFAULT_PRNG_KSZ + 2 * DEFAULT_BLK_SZ,
|
||||
.base = {
|
||||
.cra_name = "stdrng",
|
||||
.cra_driver_name = "ansi_cprng",
|
||||
.cra_priority = 100,
|
||||
.cra_ctxsize = sizeof(struct prng_context),
|
||||
.cra_module = THIS_MODULE,
|
||||
.cra_init = cprng_init,
|
||||
.cra_exit = cprng_exit,
|
||||
}
|
||||
#ifdef CONFIG_CRYPTO_FIPS
|
||||
}, {
|
||||
.cra_name = "fips(ansi_cprng)",
|
||||
.cra_driver_name = "fips_ansi_cprng",
|
||||
.cra_priority = 300,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_RNG,
|
||||
.cra_ctxsize = sizeof(struct prng_context),
|
||||
.cra_type = &crypto_rng_type,
|
||||
.cra_module = THIS_MODULE,
|
||||
.cra_init = cprng_init,
|
||||
.cra_exit = cprng_exit,
|
||||
.cra_u = {
|
||||
.rng = {
|
||||
.rng_make_random = fips_cprng_get_random,
|
||||
.rng_reset = fips_cprng_reset,
|
||||
.seedsize = DEFAULT_PRNG_KSZ + 2*DEFAULT_BLK_SZ,
|
||||
}
|
||||
.generate = fips_cprng_get_random,
|
||||
.seed = fips_cprng_reset,
|
||||
.seedsize = DEFAULT_PRNG_KSZ + 2 * DEFAULT_BLK_SZ,
|
||||
.base = {
|
||||
.cra_name = "fips(ansi_cprng)",
|
||||
.cra_driver_name = "fips_ansi_cprng",
|
||||
.cra_priority = 300,
|
||||
.cra_ctxsize = sizeof(struct prng_context),
|
||||
.cra_module = THIS_MODULE,
|
||||
.cra_init = cprng_init,
|
||||
.cra_exit = cprng_exit,
|
||||
}
|
||||
#endif
|
||||
} };
|
||||
@ -465,12 +459,12 @@ static struct crypto_alg rng_algs[] = { {
|
||||
/* Module initalization */
|
||||
static int __init prng_mod_init(void)
|
||||
{
|
||||
return crypto_register_algs(rng_algs, ARRAY_SIZE(rng_algs));
|
||||
return crypto_register_rngs(rng_algs, ARRAY_SIZE(rng_algs));
|
||||
}
|
||||
|
||||
static void __exit prng_mod_fini(void)
|
||||
{
|
||||
crypto_unregister_algs(rng_algs, ARRAY_SIZE(rng_algs));
|
||||
crypto_unregister_rngs(rng_algs, ARRAY_SIZE(rng_algs));
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <crypto/aead.h>
|
||||
#include <crypto/internal/aead.h>
|
||||
#include <crypto/internal/hash.h>
|
||||
#include <crypto/internal/skcipher.h>
|
||||
#include <crypto/authenc.h>
|
||||
@ -570,13 +570,14 @@ static int crypto_authenc_init_tfm(struct crypto_tfm *tfm)
|
||||
crypto_ahash_alignmask(auth) + 1) +
|
||||
crypto_ablkcipher_ivsize(enc);
|
||||
|
||||
tfm->crt_aead.reqsize = sizeof(struct authenc_request_ctx) +
|
||||
ctx->reqoff +
|
||||
max_t(unsigned int,
|
||||
crypto_ahash_reqsize(auth) +
|
||||
sizeof(struct ahash_request),
|
||||
sizeof(struct skcipher_givcrypt_request) +
|
||||
crypto_ablkcipher_reqsize(enc));
|
||||
crypto_aead_set_reqsize(__crypto_aead_cast(tfm),
|
||||
sizeof(struct authenc_request_ctx) +
|
||||
ctx->reqoff +
|
||||
max_t(unsigned int,
|
||||
crypto_ahash_reqsize(auth) +
|
||||
sizeof(struct ahash_request),
|
||||
sizeof(struct skcipher_givcrypt_request) +
|
||||
crypto_ablkcipher_reqsize(enc)));
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <crypto/aead.h>
|
||||
#include <crypto/internal/aead.h>
|
||||
#include <crypto/internal/hash.h>
|
||||
#include <crypto/internal/skcipher.h>
|
||||
#include <crypto/authenc.h>
|
||||
@ -662,13 +662,14 @@ static int crypto_authenc_esn_init_tfm(struct crypto_tfm *tfm)
|
||||
crypto_ahash_alignmask(auth) + 1) +
|
||||
crypto_ablkcipher_ivsize(enc);
|
||||
|
||||
tfm->crt_aead.reqsize = sizeof(struct authenc_esn_request_ctx) +
|
||||
ctx->reqoff +
|
||||
max_t(unsigned int,
|
||||
crypto_ahash_reqsize(auth) +
|
||||
sizeof(struct ahash_request),
|
||||
sizeof(struct skcipher_givcrypt_request) +
|
||||
crypto_ablkcipher_reqsize(enc));
|
||||
crypto_aead_set_reqsize(__crypto_aead_cast(tfm),
|
||||
sizeof(struct authenc_esn_request_ctx) +
|
||||
ctx->reqoff +
|
||||
max_t(unsigned int,
|
||||
crypto_ahash_reqsize(auth) +
|
||||
sizeof(struct ahash_request),
|
||||
sizeof(struct skcipher_givcrypt_request) +
|
||||
crypto_ablkcipher_reqsize(enc)));
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <crypto/aead.h>
|
||||
#include <crypto/internal/skcipher.h>
|
||||
#include <crypto/scatterwalk.h>
|
||||
#include <linux/errno.h>
|
||||
|
14
crypto/ccm.c
14
crypto/ccm.c
@ -453,9 +453,9 @@ static int crypto_ccm_init_tfm(struct crypto_tfm *tfm)
|
||||
|
||||
align = crypto_tfm_alg_alignmask(tfm);
|
||||
align &= ~(crypto_tfm_ctx_alignment() - 1);
|
||||
tfm->crt_aead.reqsize = align +
|
||||
sizeof(struct crypto_ccm_req_priv_ctx) +
|
||||
crypto_ablkcipher_reqsize(ctr);
|
||||
crypto_aead_set_reqsize(__crypto_aead_cast(tfm),
|
||||
align + sizeof(struct crypto_ccm_req_priv_ctx) +
|
||||
crypto_ablkcipher_reqsize(ctr));
|
||||
|
||||
return 0;
|
||||
|
||||
@ -729,10 +729,10 @@ static int crypto_rfc4309_init_tfm(struct crypto_tfm *tfm)
|
||||
|
||||
align = crypto_aead_alignmask(aead);
|
||||
align &= ~(crypto_tfm_ctx_alignment() - 1);
|
||||
tfm->crt_aead.reqsize = sizeof(struct aead_request) +
|
||||
ALIGN(crypto_aead_reqsize(aead),
|
||||
crypto_tfm_ctx_alignment()) +
|
||||
align + 16;
|
||||
crypto_aead_set_reqsize(__crypto_aead_cast(tfm),
|
||||
sizeof(struct aead_request) +
|
||||
ALIGN(crypto_aead_reqsize(aead), crypto_tfm_ctx_alignment()) +
|
||||
align + 16);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
216
crypto/chacha20_generic.c
Normal file
216
crypto/chacha20_generic.c
Normal file
@ -0,0 +1,216 @@
|
||||
/*
|
||||
* ChaCha20 256-bit cipher algorithm, RFC7539
|
||||
*
|
||||
* Copyright (C) 2015 Martin Willi
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <crypto/algapi.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define CHACHA20_NONCE_SIZE 16
|
||||
#define CHACHA20_KEY_SIZE 32
|
||||
#define CHACHA20_BLOCK_SIZE 64
|
||||
|
||||
struct chacha20_ctx {
|
||||
u32 key[8];
|
||||
};
|
||||
|
||||
static inline u32 rotl32(u32 v, u8 n)
|
||||
{
|
||||
return (v << n) | (v >> (sizeof(v) * 8 - n));
|
||||
}
|
||||
|
||||
static inline u32 le32_to_cpuvp(const void *p)
|
||||
{
|
||||
return le32_to_cpup(p);
|
||||
}
|
||||
|
||||
static void chacha20_block(u32 *state, void *stream)
|
||||
{
|
||||
u32 x[16], *out = stream;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(x); i++)
|
||||
x[i] = state[i];
|
||||
|
||||
for (i = 0; i < 20; i += 2) {
|
||||
x[0] += x[4]; x[12] = rotl32(x[12] ^ x[0], 16);
|
||||
x[1] += x[5]; x[13] = rotl32(x[13] ^ x[1], 16);
|
||||
x[2] += x[6]; x[14] = rotl32(x[14] ^ x[2], 16);
|
||||
x[3] += x[7]; x[15] = rotl32(x[15] ^ x[3], 16);
|
||||
|
||||
x[8] += x[12]; x[4] = rotl32(x[4] ^ x[8], 12);
|
||||
x[9] += x[13]; x[5] = rotl32(x[5] ^ x[9], 12);
|
||||
x[10] += x[14]; x[6] = rotl32(x[6] ^ x[10], 12);
|
||||
x[11] += x[15]; x[7] = rotl32(x[7] ^ x[11], 12);
|
||||
|
||||
x[0] += x[4]; x[12] = rotl32(x[12] ^ x[0], 8);
|
||||
x[1] += x[5]; x[13] = rotl32(x[13] ^ x[1], 8);
|
||||
x[2] += x[6]; x[14] = rotl32(x[14] ^ x[2], 8);
|
||||
x[3] += x[7]; x[15] = rotl32(x[15] ^ x[3], 8);
|
||||
|
||||
x[8] += x[12]; x[4] = rotl32(x[4] ^ x[8], 7);
|
||||
x[9] += x[13]; x[5] = rotl32(x[5] ^ x[9], 7);
|
||||
x[10] += x[14]; x[6] = rotl32(x[6] ^ x[10], 7);
|
||||
x[11] += x[15]; x[7] = rotl32(x[7] ^ x[11], 7);
|
||||
|
||||
x[0] += x[5]; x[15] = rotl32(x[15] ^ x[0], 16);
|
||||
x[1] += x[6]; x[12] = rotl32(x[12] ^ x[1], 16);
|
||||
x[2] += x[7]; x[13] = rotl32(x[13] ^ x[2], 16);
|
||||
x[3] += x[4]; x[14] = rotl32(x[14] ^ x[3], 16);
|
||||
|
||||
x[10] += x[15]; x[5] = rotl32(x[5] ^ x[10], 12);
|
||||
x[11] += x[12]; x[6] = rotl32(x[6] ^ x[11], 12);
|
||||
x[8] += x[13]; x[7] = rotl32(x[7] ^ x[8], 12);
|
||||
x[9] += x[14]; x[4] = rotl32(x[4] ^ x[9], 12);
|
||||
|
||||
x[0] += x[5]; x[15] = rotl32(x[15] ^ x[0], 8);
|
||||
x[1] += x[6]; x[12] = rotl32(x[12] ^ x[1], 8);
|
||||
x[2] += x[7]; x[13] = rotl32(x[13] ^ x[2], 8);
|
||||
x[3] += x[4]; x[14] = rotl32(x[14] ^ x[3], 8);
|
||||
|
||||
x[10] += x[15]; x[5] = rotl32(x[5] ^ x[10], 7);
|
||||
x[11] += x[12]; x[6] = rotl32(x[6] ^ x[11], 7);
|
||||
x[8] += x[13]; x[7] = rotl32(x[7] ^ x[8], 7);
|
||||
x[9] += x[14]; x[4] = rotl32(x[4] ^ x[9], 7);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(x); i++)
|
||||
out[i] = cpu_to_le32(x[i] + state[i]);
|
||||
|
||||
state[12]++;
|
||||
}
|
||||
|
||||
static void chacha20_docrypt(u32 *state, u8 *dst, const u8 *src,
|
||||
unsigned int bytes)
|
||||
{
|
||||
u8 stream[CHACHA20_BLOCK_SIZE];
|
||||
|
||||
if (dst != src)
|
||||
memcpy(dst, src, bytes);
|
||||
|
||||
while (bytes >= CHACHA20_BLOCK_SIZE) {
|
||||
chacha20_block(state, stream);
|
||||
crypto_xor(dst, stream, CHACHA20_BLOCK_SIZE);
|
||||
bytes -= CHACHA20_BLOCK_SIZE;
|
||||
dst += CHACHA20_BLOCK_SIZE;
|
||||
}
|
||||
if (bytes) {
|
||||
chacha20_block(state, stream);
|
||||
crypto_xor(dst, stream, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
static void chacha20_init(u32 *state, struct chacha20_ctx *ctx, u8 *iv)
|
||||
{
|
||||
static const char constant[16] = "expand 32-byte k";
|
||||
|
||||
state[0] = le32_to_cpuvp(constant + 0);
|
||||
state[1] = le32_to_cpuvp(constant + 4);
|
||||
state[2] = le32_to_cpuvp(constant + 8);
|
||||
state[3] = le32_to_cpuvp(constant + 12);
|
||||
state[4] = ctx->key[0];
|
||||
state[5] = ctx->key[1];
|
||||
state[6] = ctx->key[2];
|
||||
state[7] = ctx->key[3];
|
||||
state[8] = ctx->key[4];
|
||||
state[9] = ctx->key[5];
|
||||
state[10] = ctx->key[6];
|
||||
state[11] = ctx->key[7];
|
||||
state[12] = le32_to_cpuvp(iv + 0);
|
||||
state[13] = le32_to_cpuvp(iv + 4);
|
||||
state[14] = le32_to_cpuvp(iv + 8);
|
||||
state[15] = le32_to_cpuvp(iv + 12);
|
||||
}
|
||||
|
||||
static int chacha20_setkey(struct crypto_tfm *tfm, const u8 *key,
|
||||
unsigned int keysize)
|
||||
{
|
||||
struct chacha20_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
int i;
|
||||
|
||||
if (keysize != CHACHA20_KEY_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ctx->key); i++)
|
||||
ctx->key[i] = le32_to_cpuvp(key + i * sizeof(u32));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int chacha20_crypt(struct blkcipher_desc *desc, struct scatterlist *dst,
|
||||
struct scatterlist *src, unsigned int nbytes)
|
||||
{
|
||||
struct blkcipher_walk walk;
|
||||
u32 state[16];
|
||||
int err;
|
||||
|
||||
blkcipher_walk_init(&walk, dst, src, nbytes);
|
||||
err = blkcipher_walk_virt_block(desc, &walk, CHACHA20_BLOCK_SIZE);
|
||||
|
||||
chacha20_init(state, crypto_blkcipher_ctx(desc->tfm), walk.iv);
|
||||
|
||||
while (walk.nbytes >= CHACHA20_BLOCK_SIZE) {
|
||||
chacha20_docrypt(state, walk.dst.virt.addr, walk.src.virt.addr,
|
||||
rounddown(walk.nbytes, CHACHA20_BLOCK_SIZE));
|
||||
err = blkcipher_walk_done(desc, &walk,
|
||||
walk.nbytes % CHACHA20_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
if (walk.nbytes) {
|
||||
chacha20_docrypt(state, walk.dst.virt.addr, walk.src.virt.addr,
|
||||
walk.nbytes);
|
||||
err = blkcipher_walk_done(desc, &walk, 0);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct crypto_alg alg = {
|
||||
.cra_name = "chacha20",
|
||||
.cra_driver_name = "chacha20-generic",
|
||||
.cra_priority = 100,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER,
|
||||
.cra_blocksize = 1,
|
||||
.cra_type = &crypto_blkcipher_type,
|
||||
.cra_ctxsize = sizeof(struct chacha20_ctx),
|
||||
.cra_alignmask = sizeof(u32) - 1,
|
||||
.cra_module = THIS_MODULE,
|
||||
.cra_u = {
|
||||
.blkcipher = {
|
||||
.min_keysize = CHACHA20_KEY_SIZE,
|
||||
.max_keysize = CHACHA20_KEY_SIZE,
|
||||
.ivsize = CHACHA20_NONCE_SIZE,
|
||||
.geniv = "seqiv",
|
||||
.setkey = chacha20_setkey,
|
||||
.encrypt = chacha20_crypt,
|
||||
.decrypt = chacha20_crypt,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int __init chacha20_generic_mod_init(void)
|
||||
{
|
||||
return crypto_register_alg(&alg);
|
||||
}
|
||||
|
||||
static void __exit chacha20_generic_mod_fini(void)
|
||||
{
|
||||
crypto_unregister_alg(&alg);
|
||||
}
|
||||
|
||||
module_init(chacha20_generic_mod_init);
|
||||
module_exit(chacha20_generic_mod_fini);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
|
||||
MODULE_DESCRIPTION("chacha20 cipher algorithm");
|
||||
MODULE_ALIAS_CRYPTO("chacha20");
|
||||
MODULE_ALIAS_CRYPTO("chacha20-generic");
|
695
crypto/chacha20poly1305.c
Normal file
695
crypto/chacha20poly1305.c
Normal file
@ -0,0 +1,695 @@
|
||||
/*
|
||||
* ChaCha20-Poly1305 AEAD, RFC7539
|
||||
*
|
||||
* Copyright (C) 2015 Martin Willi
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <crypto/internal/aead.h>
|
||||
#include <crypto/internal/hash.h>
|
||||
#include <crypto/internal/skcipher.h>
|
||||
#include <crypto/scatterwalk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#define POLY1305_BLOCK_SIZE 16
|
||||
#define POLY1305_DIGEST_SIZE 16
|
||||
#define POLY1305_KEY_SIZE 32
|
||||
#define CHACHA20_KEY_SIZE 32
|
||||
#define CHACHA20_IV_SIZE 16
|
||||
#define CHACHAPOLY_IV_SIZE 12
|
||||
|
||||
struct chachapoly_instance_ctx {
|
||||
struct crypto_skcipher_spawn chacha;
|
||||
struct crypto_ahash_spawn poly;
|
||||
unsigned int saltlen;
|
||||
};
|
||||
|
||||
struct chachapoly_ctx {
|
||||
struct crypto_ablkcipher *chacha;
|
||||
struct crypto_ahash *poly;
|
||||
/* key bytes we use for the ChaCha20 IV */
|
||||
unsigned int saltlen;
|
||||
u8 salt[];
|
||||
};
|
||||
|
||||
struct poly_req {
|
||||
/* zero byte padding for AD/ciphertext, as needed */
|
||||
u8 pad[POLY1305_BLOCK_SIZE];
|
||||
/* tail data with AD/ciphertext lengths */
|
||||
struct {
|
||||
__le64 assoclen;
|
||||
__le64 cryptlen;
|
||||
} tail;
|
||||
struct scatterlist src[1];
|
||||
struct ahash_request req; /* must be last member */
|
||||
};
|
||||
|
||||
struct chacha_req {
|
||||
u8 iv[CHACHA20_IV_SIZE];
|
||||
struct scatterlist src[1];
|
||||
struct ablkcipher_request req; /* must be last member */
|
||||
};
|
||||
|
||||
struct chachapoly_req_ctx {
|
||||
/* the key we generate for Poly1305 using Chacha20 */
|
||||
u8 key[POLY1305_KEY_SIZE];
|
||||
/* calculated Poly1305 tag */
|
||||
u8 tag[POLY1305_DIGEST_SIZE];
|
||||
/* length of data to en/decrypt, without ICV */
|
||||
unsigned int cryptlen;
|
||||
union {
|
||||
struct poly_req poly;
|
||||
struct chacha_req chacha;
|
||||
} u;
|
||||
};
|
||||
|
||||
static inline void async_done_continue(struct aead_request *req, int err,
|
||||
int (*cont)(struct aead_request *))
|
||||
{
|
||||
if (!err)
|
||||
err = cont(req);
|
||||
|
||||
if (err != -EINPROGRESS && err != -EBUSY)
|
||||
aead_request_complete(req, err);
|
||||
}
|
||||
|
||||
static void chacha_iv(u8 *iv, struct aead_request *req, u32 icb)
|
||||
{
|
||||
struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
|
||||
__le32 leicb = cpu_to_le32(icb);
|
||||
|
||||
memcpy(iv, &leicb, sizeof(leicb));
|
||||
memcpy(iv + sizeof(leicb), ctx->salt, ctx->saltlen);
|
||||
memcpy(iv + sizeof(leicb) + ctx->saltlen, req->iv,
|
||||
CHACHA20_IV_SIZE - sizeof(leicb) - ctx->saltlen);
|
||||
}
|
||||
|
||||
static int poly_verify_tag(struct aead_request *req)
|
||||
{
|
||||
struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
|
||||
u8 tag[sizeof(rctx->tag)];
|
||||
|
||||
scatterwalk_map_and_copy(tag, req->src, rctx->cryptlen, sizeof(tag), 0);
|
||||
if (crypto_memneq(tag, rctx->tag, sizeof(tag)))
|
||||
return -EBADMSG;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int poly_copy_tag(struct aead_request *req)
|
||||
{
|
||||
struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
|
||||
|
||||
scatterwalk_map_and_copy(rctx->tag, req->dst, rctx->cryptlen,
|
||||
sizeof(rctx->tag), 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void chacha_decrypt_done(struct crypto_async_request *areq, int err)
|
||||
{
|
||||
async_done_continue(areq->data, err, poly_verify_tag);
|
||||
}
|
||||
|
||||
static int chacha_decrypt(struct aead_request *req)
|
||||
{
|
||||
struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
|
||||
struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
|
||||
struct chacha_req *creq = &rctx->u.chacha;
|
||||
int err;
|
||||
|
||||
chacha_iv(creq->iv, req, 1);
|
||||
|
||||
ablkcipher_request_set_callback(&creq->req, aead_request_flags(req),
|
||||
chacha_decrypt_done, req);
|
||||
ablkcipher_request_set_tfm(&creq->req, ctx->chacha);
|
||||
ablkcipher_request_set_crypt(&creq->req, req->src, req->dst,
|
||||
rctx->cryptlen, creq->iv);
|
||||
err = crypto_ablkcipher_decrypt(&creq->req);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return poly_verify_tag(req);
|
||||
}
|
||||
|
||||
static int poly_tail_continue(struct aead_request *req)
|
||||
{
|
||||
struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
|
||||
|
||||
if (rctx->cryptlen == req->cryptlen) /* encrypting */
|
||||
return poly_copy_tag(req);
|
||||
|
||||
return chacha_decrypt(req);
|
||||
}
|
||||
|
||||
static void poly_tail_done(struct crypto_async_request *areq, int err)
|
||||
{
|
||||
async_done_continue(areq->data, err, poly_tail_continue);
|
||||
}
|
||||
|
||||
static int poly_tail(struct aead_request *req)
|
||||
{
|
||||
struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
|
||||
struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
|
||||
struct poly_req *preq = &rctx->u.poly;
|
||||
__le64 len;
|
||||
int err;
|
||||
|
||||
sg_init_table(preq->src, 1);
|
||||
len = cpu_to_le64(req->assoclen);
|
||||
memcpy(&preq->tail.assoclen, &len, sizeof(len));
|
||||
len = cpu_to_le64(rctx->cryptlen);
|
||||
memcpy(&preq->tail.cryptlen, &len, sizeof(len));
|
||||
sg_set_buf(preq->src, &preq->tail, sizeof(preq->tail));
|
||||
|
||||
ahash_request_set_callback(&preq->req, aead_request_flags(req),
|
||||
poly_tail_done, req);
|
||||
ahash_request_set_tfm(&preq->req, ctx->poly);
|
||||
ahash_request_set_crypt(&preq->req, preq->src,
|
||||
rctx->tag, sizeof(preq->tail));
|
||||
|
||||
err = crypto_ahash_finup(&preq->req);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return poly_tail_continue(req);
|
||||
}
|
||||
|
||||
static void poly_cipherpad_done(struct crypto_async_request *areq, int err)
|
||||
{
|
||||
async_done_continue(areq->data, err, poly_tail);
|
||||
}
|
||||
|
||||
static int poly_cipherpad(struct aead_request *req)
|
||||
{
|
||||
struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
|
||||
struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
|
||||
struct poly_req *preq = &rctx->u.poly;
|
||||
unsigned int padlen, bs = POLY1305_BLOCK_SIZE;
|
||||
int err;
|
||||
|
||||
padlen = (bs - (rctx->cryptlen % bs)) % bs;
|
||||
memset(preq->pad, 0, sizeof(preq->pad));
|
||||
sg_init_table(preq->src, 1);
|
||||
sg_set_buf(preq->src, &preq->pad, padlen);
|
||||
|
||||
ahash_request_set_callback(&preq->req, aead_request_flags(req),
|
||||
poly_cipherpad_done, req);
|
||||
ahash_request_set_tfm(&preq->req, ctx->poly);
|
||||
ahash_request_set_crypt(&preq->req, preq->src, NULL, padlen);
|
||||
|
||||
err = crypto_ahash_update(&preq->req);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return poly_tail(req);
|
||||
}
|
||||
|
||||
static void poly_cipher_done(struct crypto_async_request *areq, int err)
|
||||
{
|
||||
async_done_continue(areq->data, err, poly_cipherpad);
|
||||
}
|
||||
|
||||
static int poly_cipher(struct aead_request *req)
|
||||
{
|
||||
struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
|
||||
struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
|
||||
struct poly_req *preq = &rctx->u.poly;
|
||||
struct scatterlist *crypt = req->src;
|
||||
int err;
|
||||
|
||||
if (rctx->cryptlen == req->cryptlen) /* encrypting */
|
||||
crypt = req->dst;
|
||||
|
||||
ahash_request_set_callback(&preq->req, aead_request_flags(req),
|
||||
poly_cipher_done, req);
|
||||
ahash_request_set_tfm(&preq->req, ctx->poly);
|
||||
ahash_request_set_crypt(&preq->req, crypt, NULL, rctx->cryptlen);
|
||||
|
||||
err = crypto_ahash_update(&preq->req);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return poly_cipherpad(req);
|
||||
}
|
||||
|
||||
static void poly_adpad_done(struct crypto_async_request *areq, int err)
|
||||
{
|
||||
async_done_continue(areq->data, err, poly_cipher);
|
||||
}
|
||||
|
||||
static int poly_adpad(struct aead_request *req)
|
||||
{
|
||||
struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
|
||||
struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
|
||||
struct poly_req *preq = &rctx->u.poly;
|
||||
unsigned int padlen, bs = POLY1305_BLOCK_SIZE;
|
||||
int err;
|
||||
|
||||
padlen = (bs - (req->assoclen % bs)) % bs;
|
||||
memset(preq->pad, 0, sizeof(preq->pad));
|
||||
sg_init_table(preq->src, 1);
|
||||
sg_set_buf(preq->src, preq->pad, padlen);
|
||||
|
||||
ahash_request_set_callback(&preq->req, aead_request_flags(req),
|
||||
poly_adpad_done, req);
|
||||
ahash_request_set_tfm(&preq->req, ctx->poly);
|
||||
ahash_request_set_crypt(&preq->req, preq->src, NULL, padlen);
|
||||
|
||||
err = crypto_ahash_update(&preq->req);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return poly_cipher(req);
|
||||
}
|
||||
|
||||
static void poly_ad_done(struct crypto_async_request *areq, int err)
|
||||
{
|
||||
async_done_continue(areq->data, err, poly_adpad);
|
||||
}
|
||||
|
||||
static int poly_ad(struct aead_request *req)
|
||||
{
|
||||
struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
|
||||
struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
|
||||
struct poly_req *preq = &rctx->u.poly;
|
||||
int err;
|
||||
|
||||
ahash_request_set_callback(&preq->req, aead_request_flags(req),
|
||||
poly_ad_done, req);
|
||||
ahash_request_set_tfm(&preq->req, ctx->poly);
|
||||
ahash_request_set_crypt(&preq->req, req->assoc, NULL, req->assoclen);
|
||||
|
||||
err = crypto_ahash_update(&preq->req);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return poly_adpad(req);
|
||||
}
|
||||
|
||||
static void poly_setkey_done(struct crypto_async_request *areq, int err)
|
||||
{
|
||||
async_done_continue(areq->data, err, poly_ad);
|
||||
}
|
||||
|
||||
static int poly_setkey(struct aead_request *req)
|
||||
{
|
||||
struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
|
||||
struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
|
||||
struct poly_req *preq = &rctx->u.poly;
|
||||
int err;
|
||||
|
||||
sg_init_table(preq->src, 1);
|
||||
sg_set_buf(preq->src, rctx->key, sizeof(rctx->key));
|
||||
|
||||
ahash_request_set_callback(&preq->req, aead_request_flags(req),
|
||||
poly_setkey_done, req);
|
||||
ahash_request_set_tfm(&preq->req, ctx->poly);
|
||||
ahash_request_set_crypt(&preq->req, preq->src, NULL, sizeof(rctx->key));
|
||||
|
||||
err = crypto_ahash_update(&preq->req);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return poly_ad(req);
|
||||
}
|
||||
|
||||
static void poly_init_done(struct crypto_async_request *areq, int err)
|
||||
{
|
||||
async_done_continue(areq->data, err, poly_setkey);
|
||||
}
|
||||
|
||||
static int poly_init(struct aead_request *req)
|
||||
{
|
||||
struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
|
||||
struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
|
||||
struct poly_req *preq = &rctx->u.poly;
|
||||
int err;
|
||||
|
||||
ahash_request_set_callback(&preq->req, aead_request_flags(req),
|
||||
poly_init_done, req);
|
||||
ahash_request_set_tfm(&preq->req, ctx->poly);
|
||||
|
||||
err = crypto_ahash_init(&preq->req);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return poly_setkey(req);
|
||||
}
|
||||
|
||||
static void poly_genkey_done(struct crypto_async_request *areq, int err)
|
||||
{
|
||||
async_done_continue(areq->data, err, poly_init);
|
||||
}
|
||||
|
||||
static int poly_genkey(struct aead_request *req)
|
||||
{
|
||||
struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
|
||||
struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
|
||||
struct chacha_req *creq = &rctx->u.chacha;
|
||||
int err;
|
||||
|
||||
sg_init_table(creq->src, 1);
|
||||
memset(rctx->key, 0, sizeof(rctx->key));
|
||||
sg_set_buf(creq->src, rctx->key, sizeof(rctx->key));
|
||||
|
||||
chacha_iv(creq->iv, req, 0);
|
||||
|
||||
ablkcipher_request_set_callback(&creq->req, aead_request_flags(req),
|
||||
poly_genkey_done, req);
|
||||
ablkcipher_request_set_tfm(&creq->req, ctx->chacha);
|
||||
ablkcipher_request_set_crypt(&creq->req, creq->src, creq->src,
|
||||
POLY1305_KEY_SIZE, creq->iv);
|
||||
|
||||
err = crypto_ablkcipher_decrypt(&creq->req);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return poly_init(req);
|
||||
}
|
||||
|
||||
static void chacha_encrypt_done(struct crypto_async_request *areq, int err)
|
||||
{
|
||||
async_done_continue(areq->data, err, poly_genkey);
|
||||
}
|
||||
|
||||
static int chacha_encrypt(struct aead_request *req)
|
||||
{
|
||||
struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
|
||||
struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
|
||||
struct chacha_req *creq = &rctx->u.chacha;
|
||||
int err;
|
||||
|
||||
chacha_iv(creq->iv, req, 1);
|
||||
|
||||
ablkcipher_request_set_callback(&creq->req, aead_request_flags(req),
|
||||
chacha_encrypt_done, req);
|
||||
ablkcipher_request_set_tfm(&creq->req, ctx->chacha);
|
||||
ablkcipher_request_set_crypt(&creq->req, req->src, req->dst,
|
||||
req->cryptlen, creq->iv);
|
||||
err = crypto_ablkcipher_encrypt(&creq->req);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return poly_genkey(req);
|
||||
}
|
||||
|
||||
static int chachapoly_encrypt(struct aead_request *req)
|
||||
{
|
||||
struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
|
||||
|
||||
rctx->cryptlen = req->cryptlen;
|
||||
|
||||
/* encrypt call chain:
|
||||
* - chacha_encrypt/done()
|
||||
* - poly_genkey/done()
|
||||
* - poly_init/done()
|
||||
* - poly_setkey/done()
|
||||
* - poly_ad/done()
|
||||
* - poly_adpad/done()
|
||||
* - poly_cipher/done()
|
||||
* - poly_cipherpad/done()
|
||||
* - poly_tail/done/continue()
|
||||
* - poly_copy_tag()
|
||||
*/
|
||||
return chacha_encrypt(req);
|
||||
}
|
||||
|
||||
static int chachapoly_decrypt(struct aead_request *req)
|
||||
{
|
||||
struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
|
||||
|
||||
if (req->cryptlen < POLY1305_DIGEST_SIZE)
|
||||
return -EINVAL;
|
||||
rctx->cryptlen = req->cryptlen - POLY1305_DIGEST_SIZE;
|
||||
|
||||
/* decrypt call chain:
|
||||
* - poly_genkey/done()
|
||||
* - poly_init/done()
|
||||
* - poly_setkey/done()
|
||||
* - poly_ad/done()
|
||||
* - poly_adpad/done()
|
||||
* - poly_cipher/done()
|
||||
* - poly_cipherpad/done()
|
||||
* - poly_tail/done/continue()
|
||||
* - chacha_decrypt/done()
|
||||
* - poly_verify_tag()
|
||||
*/
|
||||
return poly_genkey(req);
|
||||
}
|
||||
|
||||
static int chachapoly_setkey(struct crypto_aead *aead, const u8 *key,
|
||||
unsigned int keylen)
|
||||
{
|
||||
struct chachapoly_ctx *ctx = crypto_aead_ctx(aead);
|
||||
int err;
|
||||
|
||||
if (keylen != ctx->saltlen + CHACHA20_KEY_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
keylen -= ctx->saltlen;
|
||||
memcpy(ctx->salt, key + keylen, ctx->saltlen);
|
||||
|
||||
crypto_ablkcipher_clear_flags(ctx->chacha, CRYPTO_TFM_REQ_MASK);
|
||||
crypto_ablkcipher_set_flags(ctx->chacha, crypto_aead_get_flags(aead) &
|
||||
CRYPTO_TFM_REQ_MASK);
|
||||
|
||||
err = crypto_ablkcipher_setkey(ctx->chacha, key, keylen);
|
||||
crypto_aead_set_flags(aead, crypto_ablkcipher_get_flags(ctx->chacha) &
|
||||
CRYPTO_TFM_RES_MASK);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int chachapoly_setauthsize(struct crypto_aead *tfm,
|
||||
unsigned int authsize)
|
||||
{
|
||||
if (authsize != POLY1305_DIGEST_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int chachapoly_init(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct crypto_instance *inst = (void *)tfm->__crt_alg;
|
||||
struct chachapoly_instance_ctx *ictx = crypto_instance_ctx(inst);
|
||||
struct chachapoly_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
struct crypto_ablkcipher *chacha;
|
||||
struct crypto_ahash *poly;
|
||||
unsigned long align;
|
||||
|
||||
poly = crypto_spawn_ahash(&ictx->poly);
|
||||
if (IS_ERR(poly))
|
||||
return PTR_ERR(poly);
|
||||
|
||||
chacha = crypto_spawn_skcipher(&ictx->chacha);
|
||||
if (IS_ERR(chacha)) {
|
||||
crypto_free_ahash(poly);
|
||||
return PTR_ERR(chacha);
|
||||
}
|
||||
|
||||
ctx->chacha = chacha;
|
||||
ctx->poly = poly;
|
||||
ctx->saltlen = ictx->saltlen;
|
||||
|
||||
align = crypto_tfm_alg_alignmask(tfm);
|
||||
align &= ~(crypto_tfm_ctx_alignment() - 1);
|
||||
crypto_aead_set_reqsize(__crypto_aead_cast(tfm),
|
||||
align + offsetof(struct chachapoly_req_ctx, u) +
|
||||
max(offsetof(struct chacha_req, req) +
|
||||
sizeof(struct ablkcipher_request) +
|
||||
crypto_ablkcipher_reqsize(chacha),
|
||||
offsetof(struct poly_req, req) +
|
||||
sizeof(struct ahash_request) +
|
||||
crypto_ahash_reqsize(poly)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void chachapoly_exit(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct chachapoly_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
|
||||
crypto_free_ahash(ctx->poly);
|
||||
crypto_free_ablkcipher(ctx->chacha);
|
||||
}
|
||||
|
||||
static struct crypto_instance *chachapoly_alloc(struct rtattr **tb,
|
||||
const char *name,
|
||||
unsigned int ivsize)
|
||||
{
|
||||
struct crypto_attr_type *algt;
|
||||
struct crypto_instance *inst;
|
||||
struct crypto_alg *chacha;
|
||||
struct crypto_alg *poly;
|
||||
struct ahash_alg *poly_ahash;
|
||||
struct chachapoly_instance_ctx *ctx;
|
||||
const char *chacha_name, *poly_name;
|
||||
int err;
|
||||
|
||||
if (ivsize > CHACHAPOLY_IV_SIZE)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
algt = crypto_get_attr_type(tb);
|
||||
if (IS_ERR(algt))
|
||||
return ERR_CAST(algt);
|
||||
|
||||
if ((algt->type ^ CRYPTO_ALG_TYPE_AEAD) & algt->mask)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
chacha_name = crypto_attr_alg_name(tb[1]);
|
||||
if (IS_ERR(chacha_name))
|
||||
return ERR_CAST(chacha_name);
|
||||
poly_name = crypto_attr_alg_name(tb[2]);
|
||||
if (IS_ERR(poly_name))
|
||||
return ERR_CAST(poly_name);
|
||||
|
||||
poly = crypto_find_alg(poly_name, &crypto_ahash_type,
|
||||
CRYPTO_ALG_TYPE_HASH,
|
||||
CRYPTO_ALG_TYPE_AHASH_MASK);
|
||||
if (IS_ERR(poly))
|
||||
return ERR_CAST(poly);
|
||||
|
||||
err = -ENOMEM;
|
||||
inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL);
|
||||
if (!inst)
|
||||
goto out_put_poly;
|
||||
|
||||
ctx = crypto_instance_ctx(inst);
|
||||
ctx->saltlen = CHACHAPOLY_IV_SIZE - ivsize;
|
||||
poly_ahash = container_of(poly, struct ahash_alg, halg.base);
|
||||
err = crypto_init_ahash_spawn(&ctx->poly, &poly_ahash->halg, inst);
|
||||
if (err)
|
||||
goto err_free_inst;
|
||||
|
||||
crypto_set_skcipher_spawn(&ctx->chacha, inst);
|
||||
err = crypto_grab_skcipher(&ctx->chacha, chacha_name, 0,
|
||||
crypto_requires_sync(algt->type,
|
||||
algt->mask));
|
||||
if (err)
|
||||
goto err_drop_poly;
|
||||
|
||||
chacha = crypto_skcipher_spawn_alg(&ctx->chacha);
|
||||
|
||||
err = -EINVAL;
|
||||
/* Need 16-byte IV size, including Initial Block Counter value */
|
||||
if (chacha->cra_ablkcipher.ivsize != CHACHA20_IV_SIZE)
|
||||
goto out_drop_chacha;
|
||||
/* Not a stream cipher? */
|
||||
if (chacha->cra_blocksize != 1)
|
||||
goto out_drop_chacha;
|
||||
|
||||
err = -ENAMETOOLONG;
|
||||
if (snprintf(inst->alg.cra_name, CRYPTO_MAX_ALG_NAME,
|
||||
"%s(%s,%s)", name, chacha_name,
|
||||
poly_name) >= CRYPTO_MAX_ALG_NAME)
|
||||
goto out_drop_chacha;
|
||||
if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME,
|
||||
"%s(%s,%s)", name, chacha->cra_driver_name,
|
||||
poly->cra_driver_name) >= CRYPTO_MAX_ALG_NAME)
|
||||
goto out_drop_chacha;
|
||||
|
||||
inst->alg.cra_flags = CRYPTO_ALG_TYPE_AEAD;
|
||||
inst->alg.cra_flags |= (chacha->cra_flags |
|
||||
poly->cra_flags) & CRYPTO_ALG_ASYNC;
|
||||
inst->alg.cra_priority = (chacha->cra_priority +
|
||||
poly->cra_priority) / 2;
|
||||
inst->alg.cra_blocksize = 1;
|
||||
inst->alg.cra_alignmask = chacha->cra_alignmask | poly->cra_alignmask;
|
||||
inst->alg.cra_type = &crypto_nivaead_type;
|
||||
inst->alg.cra_aead.ivsize = ivsize;
|
||||
inst->alg.cra_aead.maxauthsize = POLY1305_DIGEST_SIZE;
|
||||
inst->alg.cra_ctxsize = sizeof(struct chachapoly_ctx) + ctx->saltlen;
|
||||
inst->alg.cra_init = chachapoly_init;
|
||||
inst->alg.cra_exit = chachapoly_exit;
|
||||
inst->alg.cra_aead.encrypt = chachapoly_encrypt;
|
||||
inst->alg.cra_aead.decrypt = chachapoly_decrypt;
|
||||
inst->alg.cra_aead.setkey = chachapoly_setkey;
|
||||
inst->alg.cra_aead.setauthsize = chachapoly_setauthsize;
|
||||
inst->alg.cra_aead.geniv = "seqiv";
|
||||
|
||||
out:
|
||||
crypto_mod_put(poly);
|
||||
return inst;
|
||||
|
||||
out_drop_chacha:
|
||||
crypto_drop_skcipher(&ctx->chacha);
|
||||
err_drop_poly:
|
||||
crypto_drop_ahash(&ctx->poly);
|
||||
err_free_inst:
|
||||
kfree(inst);
|
||||
out_put_poly:
|
||||
inst = ERR_PTR(err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static struct crypto_instance *rfc7539_alloc(struct rtattr **tb)
|
||||
{
|
||||
return chachapoly_alloc(tb, "rfc7539", 12);
|
||||
}
|
||||
|
||||
static struct crypto_instance *rfc7539esp_alloc(struct rtattr **tb)
|
||||
{
|
||||
return chachapoly_alloc(tb, "rfc7539esp", 8);
|
||||
}
|
||||
|
||||
static void chachapoly_free(struct crypto_instance *inst)
|
||||
{
|
||||
struct chachapoly_instance_ctx *ctx = crypto_instance_ctx(inst);
|
||||
|
||||
crypto_drop_skcipher(&ctx->chacha);
|
||||
crypto_drop_ahash(&ctx->poly);
|
||||
kfree(inst);
|
||||
}
|
||||
|
||||
static struct crypto_template rfc7539_tmpl = {
|
||||
.name = "rfc7539",
|
||||
.alloc = rfc7539_alloc,
|
||||
.free = chachapoly_free,
|
||||
.module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct crypto_template rfc7539esp_tmpl = {
|
||||
.name = "rfc7539esp",
|
||||
.alloc = rfc7539esp_alloc,
|
||||
.free = chachapoly_free,
|
||||
.module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init chacha20poly1305_module_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = crypto_register_template(&rfc7539_tmpl);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = crypto_register_template(&rfc7539esp_tmpl);
|
||||
if (err)
|
||||
crypto_unregister_template(&rfc7539_tmpl);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit chacha20poly1305_module_exit(void)
|
||||
{
|
||||
crypto_unregister_template(&rfc7539esp_tmpl);
|
||||
crypto_unregister_template(&rfc7539_tmpl);
|
||||
}
|
||||
|
||||
module_init(chacha20poly1305_module_init);
|
||||
module_exit(chacha20poly1305_module_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
|
||||
MODULE_DESCRIPTION("ChaCha20-Poly1305 AEAD");
|
||||
MODULE_ALIAS_CRYPTO("chacha20poly1305");
|
||||
MODULE_ALIAS_CRYPTO("rfc7539");
|
||||
MODULE_ALIAS_CRYPTO("rfc7539esp");
|
105
crypto/chainiv.c
105
crypto/chainiv.c
@ -80,44 +80,37 @@ unlock:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int chainiv_givencrypt_first(struct skcipher_givcrypt_request *req)
|
||||
static int chainiv_init_common(struct crypto_tfm *tfm, char iv[])
|
||||
{
|
||||
struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req);
|
||||
struct chainiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
|
||||
struct crypto_ablkcipher *geniv = __crypto_ablkcipher_cast(tfm);
|
||||
int err = 0;
|
||||
|
||||
spin_lock_bh(&ctx->lock);
|
||||
if (crypto_ablkcipher_crt(geniv)->givencrypt !=
|
||||
chainiv_givencrypt_first)
|
||||
goto unlock;
|
||||
|
||||
crypto_ablkcipher_crt(geniv)->givencrypt = chainiv_givencrypt;
|
||||
err = crypto_rng_get_bytes(crypto_default_rng, ctx->iv,
|
||||
crypto_ablkcipher_ivsize(geniv));
|
||||
|
||||
unlock:
|
||||
spin_unlock_bh(&ctx->lock);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return chainiv_givencrypt(req);
|
||||
}
|
||||
|
||||
static int chainiv_init_common(struct crypto_tfm *tfm)
|
||||
{
|
||||
tfm->crt_ablkcipher.reqsize = sizeof(struct ablkcipher_request);
|
||||
|
||||
return skcipher_geniv_init(tfm);
|
||||
if (iv) {
|
||||
err = crypto_rng_get_bytes(crypto_default_rng, iv,
|
||||
crypto_ablkcipher_ivsize(geniv));
|
||||
crypto_put_default_rng();
|
||||
}
|
||||
|
||||
return err ?: skcipher_geniv_init(tfm);
|
||||
}
|
||||
|
||||
static int chainiv_init(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct crypto_ablkcipher *geniv = __crypto_ablkcipher_cast(tfm);
|
||||
struct chainiv_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
char *iv;
|
||||
|
||||
spin_lock_init(&ctx->lock);
|
||||
|
||||
return chainiv_init_common(tfm);
|
||||
iv = NULL;
|
||||
if (!crypto_get_default_rng()) {
|
||||
crypto_ablkcipher_crt(geniv)->givencrypt = chainiv_givencrypt;
|
||||
iv = ctx->iv;
|
||||
}
|
||||
|
||||
return chainiv_init_common(tfm, iv);
|
||||
}
|
||||
|
||||
static int async_chainiv_schedule_work(struct async_chainiv_ctx *ctx)
|
||||
@ -205,33 +198,6 @@ postpone:
|
||||
return async_chainiv_postpone_request(req);
|
||||
}
|
||||
|
||||
static int async_chainiv_givencrypt_first(struct skcipher_givcrypt_request *req)
|
||||
{
|
||||
struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req);
|
||||
struct async_chainiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
|
||||
int err = 0;
|
||||
|
||||
if (test_and_set_bit(CHAINIV_STATE_INUSE, &ctx->state))
|
||||
goto out;
|
||||
|
||||
if (crypto_ablkcipher_crt(geniv)->givencrypt !=
|
||||
async_chainiv_givencrypt_first)
|
||||
goto unlock;
|
||||
|
||||
crypto_ablkcipher_crt(geniv)->givencrypt = async_chainiv_givencrypt;
|
||||
err = crypto_rng_get_bytes(crypto_default_rng, ctx->iv,
|
||||
crypto_ablkcipher_ivsize(geniv));
|
||||
|
||||
unlock:
|
||||
clear_bit(CHAINIV_STATE_INUSE, &ctx->state);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
out:
|
||||
return async_chainiv_givencrypt(req);
|
||||
}
|
||||
|
||||
static void async_chainiv_do_postponed(struct work_struct *work)
|
||||
{
|
||||
struct async_chainiv_ctx *ctx = container_of(work,
|
||||
@ -263,14 +229,23 @@ static void async_chainiv_do_postponed(struct work_struct *work)
|
||||
|
||||
static int async_chainiv_init(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct crypto_ablkcipher *geniv = __crypto_ablkcipher_cast(tfm);
|
||||
struct async_chainiv_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
char *iv;
|
||||
|
||||
spin_lock_init(&ctx->lock);
|
||||
|
||||
crypto_init_queue(&ctx->queue, 100);
|
||||
INIT_WORK(&ctx->postponed, async_chainiv_do_postponed);
|
||||
|
||||
return chainiv_init_common(tfm);
|
||||
iv = NULL;
|
||||
if (!crypto_get_default_rng()) {
|
||||
crypto_ablkcipher_crt(geniv)->givencrypt =
|
||||
async_chainiv_givencrypt;
|
||||
iv = ctx->iv;
|
||||
}
|
||||
|
||||
return chainiv_init_common(tfm, iv);
|
||||
}
|
||||
|
||||
static void async_chainiv_exit(struct crypto_tfm *tfm)
|
||||
@ -288,21 +263,14 @@ static struct crypto_instance *chainiv_alloc(struct rtattr **tb)
|
||||
{
|
||||
struct crypto_attr_type *algt;
|
||||
struct crypto_instance *inst;
|
||||
int err;
|
||||
|
||||
algt = crypto_get_attr_type(tb);
|
||||
if (IS_ERR(algt))
|
||||
return ERR_CAST(algt);
|
||||
|
||||
err = crypto_get_default_rng();
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
inst = skcipher_geniv_alloc(&chainiv_tmpl, tb, 0, 0);
|
||||
if (IS_ERR(inst))
|
||||
goto put_rng;
|
||||
|
||||
inst->alg.cra_ablkcipher.givencrypt = chainiv_givencrypt_first;
|
||||
goto out;
|
||||
|
||||
inst->alg.cra_init = chainiv_init;
|
||||
inst->alg.cra_exit = skcipher_geniv_exit;
|
||||
@ -312,9 +280,6 @@ static struct crypto_instance *chainiv_alloc(struct rtattr **tb)
|
||||
if (!crypto_requires_sync(algt->type, algt->mask)) {
|
||||
inst->alg.cra_flags |= CRYPTO_ALG_ASYNC;
|
||||
|
||||
inst->alg.cra_ablkcipher.givencrypt =
|
||||
async_chainiv_givencrypt_first;
|
||||
|
||||
inst->alg.cra_init = async_chainiv_init;
|
||||
inst->alg.cra_exit = async_chainiv_exit;
|
||||
|
||||
@ -325,22 +290,12 @@ static struct crypto_instance *chainiv_alloc(struct rtattr **tb)
|
||||
|
||||
out:
|
||||
return inst;
|
||||
|
||||
put_rng:
|
||||
crypto_put_default_rng();
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void chainiv_free(struct crypto_instance *inst)
|
||||
{
|
||||
skcipher_geniv_free(inst);
|
||||
crypto_put_default_rng();
|
||||
}
|
||||
|
||||
static struct crypto_template chainiv_tmpl = {
|
||||
.name = "chainiv",
|
||||
.alloc = chainiv_alloc,
|
||||
.free = chainiv_free,
|
||||
.free = skcipher_geniv_free,
|
||||
.module = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
135
crypto/cryptd.c
135
crypto/cryptd.c
@ -295,6 +295,23 @@ static void cryptd_blkcipher_exit_tfm(struct crypto_tfm *tfm)
|
||||
crypto_free_blkcipher(ctx->child);
|
||||
}
|
||||
|
||||
static int cryptd_init_instance(struct crypto_instance *inst,
|
||||
struct crypto_alg *alg)
|
||||
{
|
||||
if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME,
|
||||
"cryptd(%s)",
|
||||
alg->cra_driver_name) >= CRYPTO_MAX_ALG_NAME)
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
memcpy(inst->alg.cra_name, alg->cra_name, CRYPTO_MAX_ALG_NAME);
|
||||
|
||||
inst->alg.cra_priority = alg->cra_priority + 50;
|
||||
inst->alg.cra_blocksize = alg->cra_blocksize;
|
||||
inst->alg.cra_alignmask = alg->cra_alignmask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *cryptd_alloc_instance(struct crypto_alg *alg, unsigned int head,
|
||||
unsigned int tail)
|
||||
{
|
||||
@ -308,17 +325,10 @@ static void *cryptd_alloc_instance(struct crypto_alg *alg, unsigned int head,
|
||||
|
||||
inst = (void *)(p + head);
|
||||
|
||||
err = -ENAMETOOLONG;
|
||||
if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME,
|
||||
"cryptd(%s)", alg->cra_driver_name) >= CRYPTO_MAX_ALG_NAME)
|
||||
err = cryptd_init_instance(inst, alg);
|
||||
if (err)
|
||||
goto out_free_inst;
|
||||
|
||||
memcpy(inst->alg.cra_name, alg->cra_name, CRYPTO_MAX_ALG_NAME);
|
||||
|
||||
inst->alg.cra_priority = alg->cra_priority + 50;
|
||||
inst->alg.cra_blocksize = alg->cra_blocksize;
|
||||
inst->alg.cra_alignmask = alg->cra_alignmask;
|
||||
|
||||
out:
|
||||
return p;
|
||||
|
||||
@ -654,6 +664,24 @@ out_put_alg:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cryptd_aead_setkey(struct crypto_aead *parent,
|
||||
const u8 *key, unsigned int keylen)
|
||||
{
|
||||
struct cryptd_aead_ctx *ctx = crypto_aead_ctx(parent);
|
||||
struct crypto_aead *child = ctx->child;
|
||||
|
||||
return crypto_aead_setkey(child, key, keylen);
|
||||
}
|
||||
|
||||
static int cryptd_aead_setauthsize(struct crypto_aead *parent,
|
||||
unsigned int authsize)
|
||||
{
|
||||
struct cryptd_aead_ctx *ctx = crypto_aead_ctx(parent);
|
||||
struct crypto_aead *child = ctx->child;
|
||||
|
||||
return crypto_aead_setauthsize(child, authsize);
|
||||
}
|
||||
|
||||
static void cryptd_aead_crypt(struct aead_request *req,
|
||||
struct crypto_aead *child,
|
||||
int err,
|
||||
@ -715,27 +743,26 @@ static int cryptd_aead_decrypt_enqueue(struct aead_request *req)
|
||||
return cryptd_aead_enqueue(req, cryptd_aead_decrypt );
|
||||
}
|
||||
|
||||
static int cryptd_aead_init_tfm(struct crypto_tfm *tfm)
|
||||
static int cryptd_aead_init_tfm(struct crypto_aead *tfm)
|
||||
{
|
||||
struct crypto_instance *inst = crypto_tfm_alg_instance(tfm);
|
||||
struct aead_instance_ctx *ictx = crypto_instance_ctx(inst);
|
||||
struct aead_instance *inst = aead_alg_instance(tfm);
|
||||
struct aead_instance_ctx *ictx = aead_instance_ctx(inst);
|
||||
struct crypto_aead_spawn *spawn = &ictx->aead_spawn;
|
||||
struct cryptd_aead_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
struct cryptd_aead_ctx *ctx = crypto_aead_ctx(tfm);
|
||||
struct crypto_aead *cipher;
|
||||
|
||||
cipher = crypto_spawn_aead(spawn);
|
||||
if (IS_ERR(cipher))
|
||||
return PTR_ERR(cipher);
|
||||
|
||||
crypto_aead_set_flags(cipher, CRYPTO_TFM_REQ_MAY_SLEEP);
|
||||
ctx->child = cipher;
|
||||
tfm->crt_aead.reqsize = sizeof(struct cryptd_aead_request_ctx);
|
||||
crypto_aead_set_reqsize(tfm, sizeof(struct cryptd_aead_request_ctx));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cryptd_aead_exit_tfm(struct crypto_tfm *tfm)
|
||||
static void cryptd_aead_exit_tfm(struct crypto_aead *tfm)
|
||||
{
|
||||
struct cryptd_aead_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
struct cryptd_aead_ctx *ctx = crypto_aead_ctx(tfm);
|
||||
crypto_free_aead(ctx->child);
|
||||
}
|
||||
|
||||
@ -744,57 +771,57 @@ static int cryptd_create_aead(struct crypto_template *tmpl,
|
||||
struct cryptd_queue *queue)
|
||||
{
|
||||
struct aead_instance_ctx *ctx;
|
||||
struct crypto_instance *inst;
|
||||
struct crypto_alg *alg;
|
||||
u32 type = CRYPTO_ALG_TYPE_AEAD;
|
||||
u32 mask = CRYPTO_ALG_TYPE_MASK;
|
||||
struct aead_instance *inst;
|
||||
struct aead_alg *alg;
|
||||
const char *name;
|
||||
u32 type = 0;
|
||||
u32 mask = 0;
|
||||
int err;
|
||||
|
||||
cryptd_check_internal(tb, &type, &mask);
|
||||
|
||||
alg = crypto_get_attr_alg(tb, type, mask);
|
||||
if (IS_ERR(alg))
|
||||
return PTR_ERR(alg);
|
||||
name = crypto_attr_alg_name(tb[1]);
|
||||
if (IS_ERR(name))
|
||||
return PTR_ERR(name);
|
||||
|
||||
inst = cryptd_alloc_instance(alg, 0, sizeof(*ctx));
|
||||
err = PTR_ERR(inst);
|
||||
if (IS_ERR(inst))
|
||||
goto out_put_alg;
|
||||
inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL);
|
||||
if (!inst)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx = crypto_instance_ctx(inst);
|
||||
ctx = aead_instance_ctx(inst);
|
||||
ctx->queue = queue;
|
||||
|
||||
err = crypto_init_spawn(&ctx->aead_spawn.base, alg, inst,
|
||||
CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_ASYNC);
|
||||
crypto_set_aead_spawn(&ctx->aead_spawn, aead_crypto_instance(inst));
|
||||
err = crypto_grab_aead(&ctx->aead_spawn, name, type, mask);
|
||||
if (err)
|
||||
goto out_free_inst;
|
||||
|
||||
type = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC;
|
||||
if (alg->cra_flags & CRYPTO_ALG_INTERNAL)
|
||||
type |= CRYPTO_ALG_INTERNAL;
|
||||
inst->alg.cra_flags = type;
|
||||
inst->alg.cra_type = alg->cra_type;
|
||||
inst->alg.cra_ctxsize = sizeof(struct cryptd_aead_ctx);
|
||||
inst->alg.cra_init = cryptd_aead_init_tfm;
|
||||
inst->alg.cra_exit = cryptd_aead_exit_tfm;
|
||||
inst->alg.cra_aead.setkey = alg->cra_aead.setkey;
|
||||
inst->alg.cra_aead.setauthsize = alg->cra_aead.setauthsize;
|
||||
inst->alg.cra_aead.geniv = alg->cra_aead.geniv;
|
||||
inst->alg.cra_aead.ivsize = alg->cra_aead.ivsize;
|
||||
inst->alg.cra_aead.maxauthsize = alg->cra_aead.maxauthsize;
|
||||
inst->alg.cra_aead.encrypt = cryptd_aead_encrypt_enqueue;
|
||||
inst->alg.cra_aead.decrypt = cryptd_aead_decrypt_enqueue;
|
||||
inst->alg.cra_aead.givencrypt = alg->cra_aead.givencrypt;
|
||||
inst->alg.cra_aead.givdecrypt = alg->cra_aead.givdecrypt;
|
||||
alg = crypto_spawn_aead_alg(&ctx->aead_spawn);
|
||||
err = cryptd_init_instance(aead_crypto_instance(inst), &alg->base);
|
||||
if (err)
|
||||
goto out_drop_aead;
|
||||
|
||||
err = crypto_register_instance(tmpl, inst);
|
||||
inst->alg.base.cra_flags = CRYPTO_ALG_ASYNC |
|
||||
(alg->base.cra_flags & CRYPTO_ALG_INTERNAL);
|
||||
inst->alg.base.cra_ctxsize = sizeof(struct cryptd_aead_ctx);
|
||||
|
||||
inst->alg.ivsize = crypto_aead_alg_ivsize(alg);
|
||||
inst->alg.maxauthsize = crypto_aead_alg_maxauthsize(alg);
|
||||
|
||||
inst->alg.init = cryptd_aead_init_tfm;
|
||||
inst->alg.exit = cryptd_aead_exit_tfm;
|
||||
inst->alg.setkey = cryptd_aead_setkey;
|
||||
inst->alg.setauthsize = cryptd_aead_setauthsize;
|
||||
inst->alg.encrypt = cryptd_aead_encrypt_enqueue;
|
||||
inst->alg.decrypt = cryptd_aead_decrypt_enqueue;
|
||||
|
||||
err = aead_register_instance(tmpl, inst);
|
||||
if (err) {
|
||||
crypto_drop_spawn(&ctx->aead_spawn.base);
|
||||
out_drop_aead:
|
||||
crypto_drop_aead(&ctx->aead_spawn);
|
||||
out_free_inst:
|
||||
kfree(inst);
|
||||
}
|
||||
out_put_alg:
|
||||
crypto_mod_put(alg);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -832,8 +859,8 @@ static void cryptd_free(struct crypto_instance *inst)
|
||||
kfree(ahash_instance(inst));
|
||||
return;
|
||||
case CRYPTO_ALG_TYPE_AEAD:
|
||||
crypto_drop_spawn(&aead_ctx->aead_spawn.base);
|
||||
kfree(inst);
|
||||
crypto_drop_aead(&aead_ctx->aead_spawn);
|
||||
kfree(aead_instance(inst));
|
||||
return;
|
||||
default:
|
||||
crypto_drop_spawn(&ctx->spawn);
|
||||
|
@ -25,6 +25,10 @@
|
||||
#include <linux/mm.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
static DEFINE_MUTEX(crypto_default_null_skcipher_lock);
|
||||
static struct crypto_blkcipher *crypto_default_null_skcipher;
|
||||
static int crypto_default_null_skcipher_refcnt;
|
||||
|
||||
static int null_compress(struct crypto_tfm *tfm, const u8 *src,
|
||||
unsigned int slen, u8 *dst, unsigned int *dlen)
|
||||
{
|
||||
@ -149,6 +153,41 @@ MODULE_ALIAS_CRYPTO("compress_null");
|
||||
MODULE_ALIAS_CRYPTO("digest_null");
|
||||
MODULE_ALIAS_CRYPTO("cipher_null");
|
||||
|
||||
struct crypto_blkcipher *crypto_get_default_null_skcipher(void)
|
||||
{
|
||||
struct crypto_blkcipher *tfm;
|
||||
|
||||
mutex_lock(&crypto_default_null_skcipher_lock);
|
||||
tfm = crypto_default_null_skcipher;
|
||||
|
||||
if (!tfm) {
|
||||
tfm = crypto_alloc_blkcipher("ecb(cipher_null)", 0, 0);
|
||||
if (IS_ERR(tfm))
|
||||
goto unlock;
|
||||
|
||||
crypto_default_null_skcipher = tfm;
|
||||
}
|
||||
|
||||
crypto_default_null_skcipher_refcnt++;
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&crypto_default_null_skcipher_lock);
|
||||
|
||||
return tfm;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_get_default_null_skcipher);
|
||||
|
||||
void crypto_put_default_null_skcipher(void)
|
||||
{
|
||||
mutex_lock(&crypto_default_null_skcipher_lock);
|
||||
if (!--crypto_default_null_skcipher_refcnt) {
|
||||
crypto_free_blkcipher(crypto_default_null_skcipher);
|
||||
crypto_default_null_skcipher = NULL;
|
||||
}
|
||||
mutex_unlock(&crypto_default_null_skcipher_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_put_default_null_skcipher);
|
||||
|
||||
static int __init crypto_null_mod_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include <net/net_namespace.h>
|
||||
#include <crypto/internal/aead.h>
|
||||
#include <crypto/internal/skcipher.h>
|
||||
#include <crypto/internal/rng.h>
|
||||
#include <crypto/akcipher.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
@ -110,6 +112,21 @@ nla_put_failure:
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static int crypto_report_akcipher(struct sk_buff *skb, struct crypto_alg *alg)
|
||||
{
|
||||
struct crypto_report_akcipher rakcipher;
|
||||
|
||||
strncpy(rakcipher.type, "akcipher", sizeof(rakcipher.type));
|
||||
|
||||
if (nla_put(skb, CRYPTOCFGA_REPORT_AKCIPHER,
|
||||
sizeof(struct crypto_report_akcipher), &rakcipher))
|
||||
goto nla_put_failure;
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static int crypto_report_one(struct crypto_alg *alg,
|
||||
struct crypto_user_alg *ualg, struct sk_buff *skb)
|
||||
{
|
||||
@ -154,6 +171,12 @@ static int crypto_report_one(struct crypto_alg *alg,
|
||||
goto nla_put_failure;
|
||||
|
||||
break;
|
||||
|
||||
case CRYPTO_ALG_TYPE_AKCIPHER:
|
||||
if (crypto_report_akcipher(skb, alg))
|
||||
goto nla_put_failure;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
@ -450,13 +473,21 @@ static int crypto_add_alg(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int crypto_del_rng(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
struct nlattr **attrs)
|
||||
{
|
||||
if (!netlink_capable(skb, CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
return crypto_del_default_rng();
|
||||
}
|
||||
|
||||
#define MSGSIZE(type) sizeof(struct type)
|
||||
|
||||
static const int crypto_msg_min[CRYPTO_NR_MSGTYPES] = {
|
||||
[CRYPTO_MSG_NEWALG - CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
|
||||
[CRYPTO_MSG_DELALG - CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
|
||||
[CRYPTO_MSG_UPDATEALG - CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
|
||||
[CRYPTO_MSG_GETALG - CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
|
||||
[CRYPTO_MSG_DELRNG - CRYPTO_MSG_BASE] = 0,
|
||||
};
|
||||
|
||||
static const struct nla_policy crypto_policy[CRYPTOCFGA_MAX+1] = {
|
||||
@ -476,6 +507,7 @@ static const struct crypto_link {
|
||||
[CRYPTO_MSG_GETALG - CRYPTO_MSG_BASE] = { .doit = crypto_report,
|
||||
.dump = crypto_dump_report,
|
||||
.done = crypto_dump_report_done},
|
||||
[CRYPTO_MSG_DELRNG - CRYPTO_MSG_BASE] = { .doit = crypto_del_rng },
|
||||
};
|
||||
|
||||
static int crypto_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||
|
565
crypto/drbg.c
565
crypto/drbg.c
@ -98,6 +98,7 @@
|
||||
*/
|
||||
|
||||
#include <crypto/drbg.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
/***************************************************************
|
||||
* Backend cipher definitions available to DRBG
|
||||
@ -190,6 +191,8 @@ static const struct drbg_core drbg_cores[] = {
|
||||
#endif /* CONFIG_CRYPTO_DRBG_HMAC */
|
||||
};
|
||||
|
||||
static int drbg_uninstantiate(struct drbg_state *drbg);
|
||||
|
||||
/******************************************************************
|
||||
* Generic helper functions
|
||||
******************************************************************/
|
||||
@ -235,7 +238,7 @@ static bool drbg_fips_continuous_test(struct drbg_state *drbg,
|
||||
#ifdef CONFIG_CRYPTO_FIPS
|
||||
int ret = 0;
|
||||
/* skip test if we test the overall system */
|
||||
if (drbg->test_data)
|
||||
if (list_empty(&drbg->test_data.list))
|
||||
return true;
|
||||
/* only perform test in FIPS mode */
|
||||
if (0 == fips_enabled)
|
||||
@ -487,7 +490,7 @@ static int drbg_ctr_df(struct drbg_state *drbg,
|
||||
|
||||
out:
|
||||
memset(iv, 0, drbg_blocklen(drbg));
|
||||
memset(temp, 0, drbg_statelen(drbg));
|
||||
memset(temp, 0, drbg_statelen(drbg) + drbg_blocklen(drbg));
|
||||
memset(pad, 0, drbg_blocklen(drbg));
|
||||
return ret;
|
||||
}
|
||||
@ -1041,6 +1044,58 @@ static struct drbg_state_ops drbg_hash_ops = {
|
||||
* Functions common for DRBG implementations
|
||||
******************************************************************/
|
||||
|
||||
static inline int __drbg_seed(struct drbg_state *drbg, struct list_head *seed,
|
||||
int reseed)
|
||||
{
|
||||
int ret = drbg->d_ops->update(drbg, seed, reseed);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drbg->seeded = true;
|
||||
/* 10.1.1.2 / 10.1.1.3 step 5 */
|
||||
drbg->reseed_ctr = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void drbg_async_seed(struct work_struct *work)
|
||||
{
|
||||
struct drbg_string data;
|
||||
LIST_HEAD(seedlist);
|
||||
struct drbg_state *drbg = container_of(work, struct drbg_state,
|
||||
seed_work);
|
||||
unsigned int entropylen = drbg_sec_strength(drbg->core->flags);
|
||||
unsigned char entropy[32];
|
||||
|
||||
BUG_ON(!entropylen);
|
||||
BUG_ON(entropylen > sizeof(entropy));
|
||||
get_random_bytes(entropy, entropylen);
|
||||
|
||||
drbg_string_fill(&data, entropy, entropylen);
|
||||
list_add_tail(&data.list, &seedlist);
|
||||
|
||||
mutex_lock(&drbg->drbg_mutex);
|
||||
|
||||
/* If nonblocking pool is initialized, deactivate Jitter RNG */
|
||||
crypto_free_rng(drbg->jent);
|
||||
drbg->jent = NULL;
|
||||
|
||||
/* Set seeded to false so that if __drbg_seed fails the
|
||||
* next generate call will trigger a reseed.
|
||||
*/
|
||||
drbg->seeded = false;
|
||||
|
||||
__drbg_seed(drbg, &seedlist, true);
|
||||
|
||||
if (drbg->seeded)
|
||||
drbg->reseed_threshold = drbg_max_requests(drbg);
|
||||
|
||||
mutex_unlock(&drbg->drbg_mutex);
|
||||
|
||||
memzero_explicit(entropy, entropylen);
|
||||
}
|
||||
|
||||
/*
|
||||
* Seeding or reseeding of the DRBG
|
||||
*
|
||||
@ -1055,9 +1110,9 @@ static struct drbg_state_ops drbg_hash_ops = {
|
||||
static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,
|
||||
bool reseed)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned char *entropy = NULL;
|
||||
size_t entropylen = 0;
|
||||
int ret;
|
||||
unsigned char entropy[((32 + 16) * 2)];
|
||||
unsigned int entropylen = drbg_sec_strength(drbg->core->flags);
|
||||
struct drbg_string data1;
|
||||
LIST_HEAD(seedlist);
|
||||
|
||||
@ -1068,31 +1123,45 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (drbg->test_data && drbg->test_data->testentropy) {
|
||||
drbg_string_fill(&data1, drbg->test_data->testentropy->buf,
|
||||
drbg->test_data->testentropy->len);
|
||||
if (list_empty(&drbg->test_data.list)) {
|
||||
drbg_string_fill(&data1, drbg->test_data.buf,
|
||||
drbg->test_data.len);
|
||||
pr_devel("DRBG: using test entropy\n");
|
||||
} else {
|
||||
/*
|
||||
* Gather entropy equal to the security strength of the DRBG.
|
||||
* With a derivation function, a nonce is required in addition
|
||||
* to the entropy. A nonce must be at least 1/2 of the security
|
||||
* strength of the DRBG in size. Thus, entropy * nonce is 3/2
|
||||
* strength of the DRBG in size. Thus, entropy + nonce is 3/2
|
||||
* of the strength. The consideration of a nonce is only
|
||||
* applicable during initial seeding.
|
||||
*/
|
||||
entropylen = drbg_sec_strength(drbg->core->flags);
|
||||
if (!entropylen)
|
||||
return -EFAULT;
|
||||
BUG_ON(!entropylen);
|
||||
if (!reseed)
|
||||
entropylen = ((entropylen + 1) / 2) * 3;
|
||||
pr_devel("DRBG: (re)seeding with %zu bytes of entropy\n",
|
||||
entropylen);
|
||||
entropy = kzalloc(entropylen, GFP_KERNEL);
|
||||
if (!entropy)
|
||||
return -ENOMEM;
|
||||
BUG_ON((entropylen * 2) > sizeof(entropy));
|
||||
|
||||
/* Get seed from in-kernel /dev/urandom */
|
||||
get_random_bytes(entropy, entropylen);
|
||||
drbg_string_fill(&data1, entropy, entropylen);
|
||||
|
||||
if (!drbg->jent) {
|
||||
drbg_string_fill(&data1, entropy, entropylen);
|
||||
pr_devel("DRBG: (re)seeding with %u bytes of entropy\n",
|
||||
entropylen);
|
||||
} else {
|
||||
/* Get seed from Jitter RNG */
|
||||
ret = crypto_rng_get_bytes(drbg->jent,
|
||||
entropy + entropylen,
|
||||
entropylen);
|
||||
if (ret) {
|
||||
pr_devel("DRBG: jent failed with %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drbg_string_fill(&data1, entropy, entropylen * 2);
|
||||
pr_devel("DRBG: (re)seeding with %u bytes of entropy\n",
|
||||
entropylen * 2);
|
||||
}
|
||||
}
|
||||
list_add_tail(&data1.list, &seedlist);
|
||||
|
||||
@ -1111,16 +1180,10 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,
|
||||
memset(drbg->C, 0, drbg_statelen(drbg));
|
||||
}
|
||||
|
||||
ret = drbg->d_ops->update(drbg, &seedlist, reseed);
|
||||
if (ret)
|
||||
goto out;
|
||||
ret = __drbg_seed(drbg, &seedlist, reseed);
|
||||
|
||||
drbg->seeded = true;
|
||||
/* 10.1.1.2 / 10.1.1.3 step 5 */
|
||||
drbg->reseed_ctr = 1;
|
||||
memzero_explicit(entropy, entropylen * 2);
|
||||
|
||||
out:
|
||||
kzfree(entropy);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1136,6 +1199,8 @@ static inline void drbg_dealloc_state(struct drbg_state *drbg)
|
||||
kzfree(drbg->scratchpad);
|
||||
drbg->scratchpad = NULL;
|
||||
drbg->reseed_ctr = 0;
|
||||
drbg->d_ops = NULL;
|
||||
drbg->core = NULL;
|
||||
#ifdef CONFIG_CRYPTO_FIPS
|
||||
kzfree(drbg->prev);
|
||||
drbg->prev = NULL;
|
||||
@ -1152,6 +1217,27 @@ static inline int drbg_alloc_state(struct drbg_state *drbg)
|
||||
int ret = -ENOMEM;
|
||||
unsigned int sb_size = 0;
|
||||
|
||||
switch (drbg->core->flags & DRBG_TYPE_MASK) {
|
||||
#ifdef CONFIG_CRYPTO_DRBG_HMAC
|
||||
case DRBG_HMAC:
|
||||
drbg->d_ops = &drbg_hmac_ops;
|
||||
break;
|
||||
#endif /* CONFIG_CRYPTO_DRBG_HMAC */
|
||||
#ifdef CONFIG_CRYPTO_DRBG_HASH
|
||||
case DRBG_HASH:
|
||||
drbg->d_ops = &drbg_hash_ops;
|
||||
break;
|
||||
#endif /* CONFIG_CRYPTO_DRBG_HASH */
|
||||
#ifdef CONFIG_CRYPTO_DRBG_CTR
|
||||
case DRBG_CTR:
|
||||
drbg->d_ops = &drbg_ctr_ops;
|
||||
break;
|
||||
#endif /* CONFIG_CRYPTO_DRBG_CTR */
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
goto err;
|
||||
}
|
||||
|
||||
drbg->V = kmalloc(drbg_statelen(drbg), GFP_KERNEL);
|
||||
if (!drbg->V)
|
||||
goto err;
|
||||
@ -1181,7 +1267,7 @@ static inline int drbg_alloc_state(struct drbg_state *drbg)
|
||||
if (!drbg->scratchpad)
|
||||
goto err;
|
||||
}
|
||||
spin_lock_init(&drbg->drbg_lock);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
@ -1189,79 +1275,6 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Strategy to avoid holding long term locks: generate a shadow copy of DRBG
|
||||
* and perform all operations on this shadow copy. After finishing, restore
|
||||
* the updated state of the shadow copy into original drbg state. This way,
|
||||
* only the read and write operations of the original drbg state must be
|
||||
* locked
|
||||
*/
|
||||
static inline void drbg_copy_drbg(struct drbg_state *src,
|
||||
struct drbg_state *dst)
|
||||
{
|
||||
if (!src || !dst)
|
||||
return;
|
||||
memcpy(dst->V, src->V, drbg_statelen(src));
|
||||
memcpy(dst->C, src->C, drbg_statelen(src));
|
||||
dst->reseed_ctr = src->reseed_ctr;
|
||||
dst->seeded = src->seeded;
|
||||
dst->pr = src->pr;
|
||||
#ifdef CONFIG_CRYPTO_FIPS
|
||||
dst->fips_primed = src->fips_primed;
|
||||
memcpy(dst->prev, src->prev, drbg_blocklen(src));
|
||||
#endif
|
||||
/*
|
||||
* Not copied:
|
||||
* scratchpad is initialized drbg_alloc_state;
|
||||
* priv_data is initialized with call to crypto_init;
|
||||
* d_ops and core are set outside, as these parameters are const;
|
||||
* test_data is set outside to prevent it being copied back.
|
||||
*/
|
||||
}
|
||||
|
||||
static int drbg_make_shadow(struct drbg_state *drbg, struct drbg_state **shadow)
|
||||
{
|
||||
int ret = -ENOMEM;
|
||||
struct drbg_state *tmp = NULL;
|
||||
|
||||
tmp = kzalloc(sizeof(struct drbg_state), GFP_KERNEL);
|
||||
if (!tmp)
|
||||
return -ENOMEM;
|
||||
|
||||
/* read-only data as they are defined as const, no lock needed */
|
||||
tmp->core = drbg->core;
|
||||
tmp->d_ops = drbg->d_ops;
|
||||
|
||||
ret = drbg_alloc_state(tmp);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
spin_lock_bh(&drbg->drbg_lock);
|
||||
drbg_copy_drbg(drbg, tmp);
|
||||
/* only make a link to the test buffer, as we only read that data */
|
||||
tmp->test_data = drbg->test_data;
|
||||
spin_unlock_bh(&drbg->drbg_lock);
|
||||
*shadow = tmp;
|
||||
return 0;
|
||||
|
||||
err:
|
||||
kzfree(tmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void drbg_restore_shadow(struct drbg_state *drbg,
|
||||
struct drbg_state **shadow)
|
||||
{
|
||||
struct drbg_state *tmp = *shadow;
|
||||
|
||||
spin_lock_bh(&drbg->drbg_lock);
|
||||
drbg_copy_drbg(tmp, drbg);
|
||||
spin_unlock_bh(&drbg->drbg_lock);
|
||||
drbg_dealloc_state(tmp);
|
||||
kzfree(tmp);
|
||||
*shadow = NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* DRBG interface functions
|
||||
*************************************************************************/
|
||||
@ -1287,14 +1300,12 @@ static int drbg_generate(struct drbg_state *drbg,
|
||||
struct drbg_string *addtl)
|
||||
{
|
||||
int len = 0;
|
||||
struct drbg_state *shadow = NULL;
|
||||
LIST_HEAD(addtllist);
|
||||
struct drbg_string timestamp;
|
||||
union {
|
||||
cycles_t cycles;
|
||||
unsigned char char_cycles[sizeof(cycles_t)];
|
||||
} now;
|
||||
|
||||
if (!drbg->core) {
|
||||
pr_devel("DRBG: not yet seeded\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (0 == buflen || !buf) {
|
||||
pr_devel("DRBG: no output buffer provided\n");
|
||||
return -EINVAL;
|
||||
@ -1304,15 +1315,9 @@ static int drbg_generate(struct drbg_state *drbg,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
len = drbg_make_shadow(drbg, &shadow);
|
||||
if (len) {
|
||||
pr_devel("DRBG: shadow copy cannot be generated\n");
|
||||
return len;
|
||||
}
|
||||
|
||||
/* 9.3.1 step 2 */
|
||||
len = -EINVAL;
|
||||
if (buflen > (drbg_max_request_bytes(shadow))) {
|
||||
if (buflen > (drbg_max_request_bytes(drbg))) {
|
||||
pr_devel("DRBG: requested random numbers too large %u\n",
|
||||
buflen);
|
||||
goto err;
|
||||
@ -1321,7 +1326,7 @@ static int drbg_generate(struct drbg_state *drbg,
|
||||
/* 9.3.1 step 3 is implicit with the chosen DRBG */
|
||||
|
||||
/* 9.3.1 step 4 */
|
||||
if (addtl && addtl->len > (drbg_max_addtl(shadow))) {
|
||||
if (addtl && addtl->len > (drbg_max_addtl(drbg))) {
|
||||
pr_devel("DRBG: additional information string too long %zu\n",
|
||||
addtl->len);
|
||||
goto err;
|
||||
@ -1332,46 +1337,29 @@ static int drbg_generate(struct drbg_state *drbg,
|
||||
* 9.3.1 step 6 and 9 supplemented by 9.3.2 step c is implemented
|
||||
* here. The spec is a bit convoluted here, we make it simpler.
|
||||
*/
|
||||
if ((drbg_max_requests(shadow)) < shadow->reseed_ctr)
|
||||
shadow->seeded = false;
|
||||
if (drbg->reseed_threshold < drbg->reseed_ctr)
|
||||
drbg->seeded = false;
|
||||
|
||||
/* allocate cipher handle */
|
||||
len = shadow->d_ops->crypto_init(shadow);
|
||||
if (len)
|
||||
goto err;
|
||||
|
||||
if (shadow->pr || !shadow->seeded) {
|
||||
if (drbg->pr || !drbg->seeded) {
|
||||
pr_devel("DRBG: reseeding before generation (prediction "
|
||||
"resistance: %s, state %s)\n",
|
||||
drbg->pr ? "true" : "false",
|
||||
drbg->seeded ? "seeded" : "unseeded");
|
||||
/* 9.3.1 steps 7.1 through 7.3 */
|
||||
len = drbg_seed(shadow, addtl, true);
|
||||
len = drbg_seed(drbg, addtl, true);
|
||||
if (len)
|
||||
goto err;
|
||||
/* 9.3.1 step 7.4 */
|
||||
addtl = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mix the time stamp into the DRBG state if the DRBG is not in
|
||||
* test mode. If there are two callers invoking the DRBG at the same
|
||||
* time, i.e. before the first caller merges its shadow state back,
|
||||
* both callers would obtain the same random number stream without
|
||||
* changing the state here.
|
||||
*/
|
||||
if (!drbg->test_data) {
|
||||
now.cycles = random_get_entropy();
|
||||
drbg_string_fill(×tamp, now.char_cycles, sizeof(cycles_t));
|
||||
list_add_tail(×tamp.list, &addtllist);
|
||||
}
|
||||
if (addtl && 0 < addtl->len)
|
||||
list_add_tail(&addtl->list, &addtllist);
|
||||
/* 9.3.1 step 8 and 10 */
|
||||
len = shadow->d_ops->generate(shadow, buf, buflen, &addtllist);
|
||||
len = drbg->d_ops->generate(drbg, buf, buflen, &addtllist);
|
||||
|
||||
/* 10.1.1.4 step 6, 10.1.2.5 step 7, 10.2.1.5.2 step 7 */
|
||||
shadow->reseed_ctr++;
|
||||
drbg->reseed_ctr++;
|
||||
if (0 >= len)
|
||||
goto err;
|
||||
|
||||
@ -1391,7 +1379,7 @@ static int drbg_generate(struct drbg_state *drbg,
|
||||
* case somebody has a need to implement the test of 11.3.3.
|
||||
*/
|
||||
#if 0
|
||||
if (shadow->reseed_ctr && !(shadow->reseed_ctr % 4096)) {
|
||||
if (drbg->reseed_ctr && !(drbg->reseed_ctr % 4096)) {
|
||||
int err = 0;
|
||||
pr_devel("DRBG: start to perform self test\n");
|
||||
if (drbg->core->flags & DRBG_HMAC)
|
||||
@ -1410,8 +1398,6 @@ static int drbg_generate(struct drbg_state *drbg,
|
||||
* are returned when reusing this DRBG cipher handle
|
||||
*/
|
||||
drbg_uninstantiate(drbg);
|
||||
drbg_dealloc_state(shadow);
|
||||
kzfree(shadow);
|
||||
return 0;
|
||||
} else {
|
||||
pr_devel("DRBG: self test successful\n");
|
||||
@ -1425,8 +1411,6 @@ static int drbg_generate(struct drbg_state *drbg,
|
||||
*/
|
||||
len = 0;
|
||||
err:
|
||||
shadow->d_ops->crypto_fini(shadow);
|
||||
drbg_restore_shadow(drbg, &shadow);
|
||||
return len;
|
||||
}
|
||||
|
||||
@ -1442,19 +1426,68 @@ static int drbg_generate_long(struct drbg_state *drbg,
|
||||
unsigned char *buf, unsigned int buflen,
|
||||
struct drbg_string *addtl)
|
||||
{
|
||||
int len = 0;
|
||||
unsigned int len = 0;
|
||||
unsigned int slice = 0;
|
||||
do {
|
||||
int tmplen = 0;
|
||||
int err = 0;
|
||||
unsigned int chunk = 0;
|
||||
slice = ((buflen - len) / drbg_max_request_bytes(drbg));
|
||||
chunk = slice ? drbg_max_request_bytes(drbg) : (buflen - len);
|
||||
tmplen = drbg_generate(drbg, buf + len, chunk, addtl);
|
||||
if (0 >= tmplen)
|
||||
return tmplen;
|
||||
len += tmplen;
|
||||
mutex_lock(&drbg->drbg_mutex);
|
||||
err = drbg_generate(drbg, buf + len, chunk, addtl);
|
||||
mutex_unlock(&drbg->drbg_mutex);
|
||||
if (0 > err)
|
||||
return err;
|
||||
len += chunk;
|
||||
} while (slice > 0 && (len < buflen));
|
||||
return len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void drbg_schedule_async_seed(struct random_ready_callback *rdy)
|
||||
{
|
||||
struct drbg_state *drbg = container_of(rdy, struct drbg_state,
|
||||
random_ready);
|
||||
|
||||
schedule_work(&drbg->seed_work);
|
||||
}
|
||||
|
||||
static int drbg_prepare_hrng(struct drbg_state *drbg)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* We do not need an HRNG in test mode. */
|
||||
if (list_empty(&drbg->test_data.list))
|
||||
return 0;
|
||||
|
||||
INIT_WORK(&drbg->seed_work, drbg_async_seed);
|
||||
|
||||
drbg->random_ready.owner = THIS_MODULE;
|
||||
drbg->random_ready.func = drbg_schedule_async_seed;
|
||||
|
||||
err = add_random_ready_callback(&drbg->random_ready);
|
||||
|
||||
switch (err) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case -EALREADY:
|
||||
err = 0;
|
||||
/* fall through */
|
||||
|
||||
default:
|
||||
drbg->random_ready.func = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
drbg->jent = crypto_alloc_rng("jitterentropy_rng", 0, 0);
|
||||
|
||||
/*
|
||||
* Require frequent reseeds until the seed source is fully
|
||||
* initialized.
|
||||
*/
|
||||
drbg->reseed_threshold = 50;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1477,32 +1510,12 @@ static int drbg_generate_long(struct drbg_state *drbg,
|
||||
static int drbg_instantiate(struct drbg_state *drbg, struct drbg_string *pers,
|
||||
int coreref, bool pr)
|
||||
{
|
||||
int ret = -ENOMEM;
|
||||
int ret;
|
||||
bool reseed = true;
|
||||
|
||||
pr_devel("DRBG: Initializing DRBG core %d with prediction resistance "
|
||||
"%s\n", coreref, pr ? "enabled" : "disabled");
|
||||
drbg->core = &drbg_cores[coreref];
|
||||
drbg->pr = pr;
|
||||
drbg->seeded = false;
|
||||
switch (drbg->core->flags & DRBG_TYPE_MASK) {
|
||||
#ifdef CONFIG_CRYPTO_DRBG_HMAC
|
||||
case DRBG_HMAC:
|
||||
drbg->d_ops = &drbg_hmac_ops;
|
||||
break;
|
||||
#endif /* CONFIG_CRYPTO_DRBG_HMAC */
|
||||
#ifdef CONFIG_CRYPTO_DRBG_HASH
|
||||
case DRBG_HASH:
|
||||
drbg->d_ops = &drbg_hash_ops;
|
||||
break;
|
||||
#endif /* CONFIG_CRYPTO_DRBG_HASH */
|
||||
#ifdef CONFIG_CRYPTO_DRBG_CTR
|
||||
case DRBG_CTR:
|
||||
drbg->d_ops = &drbg_ctr_ops;
|
||||
break;
|
||||
#endif /* CONFIG_CRYPTO_DRBG_CTR */
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
mutex_lock(&drbg->drbg_mutex);
|
||||
|
||||
/* 9.1 step 1 is implicit with the selected DRBG type */
|
||||
|
||||
@ -1514,22 +1527,52 @@ static int drbg_instantiate(struct drbg_state *drbg, struct drbg_string *pers,
|
||||
|
||||
/* 9.1 step 4 is implicit in drbg_sec_strength */
|
||||
|
||||
ret = drbg_alloc_state(drbg);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!drbg->core) {
|
||||
drbg->core = &drbg_cores[coreref];
|
||||
drbg->pr = pr;
|
||||
drbg->seeded = false;
|
||||
drbg->reseed_threshold = drbg_max_requests(drbg);
|
||||
|
||||
ret = -EFAULT;
|
||||
if (drbg->d_ops->crypto_init(drbg))
|
||||
goto err;
|
||||
ret = drbg_seed(drbg, pers, false);
|
||||
drbg->d_ops->crypto_fini(drbg);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = drbg_alloc_state(drbg);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
return 0;
|
||||
ret = -EFAULT;
|
||||
if (drbg->d_ops->crypto_init(drbg))
|
||||
goto err;
|
||||
|
||||
ret = drbg_prepare_hrng(drbg);
|
||||
if (ret)
|
||||
goto free_everything;
|
||||
|
||||
if (IS_ERR(drbg->jent)) {
|
||||
ret = PTR_ERR(drbg->jent);
|
||||
drbg->jent = NULL;
|
||||
if (fips_enabled || ret != -ENOENT)
|
||||
goto free_everything;
|
||||
pr_info("DRBG: Continuing without Jitter RNG\n");
|
||||
}
|
||||
|
||||
reseed = false;
|
||||
}
|
||||
|
||||
ret = drbg_seed(drbg, pers, reseed);
|
||||
|
||||
if (ret && !reseed)
|
||||
goto free_everything;
|
||||
|
||||
mutex_unlock(&drbg->drbg_mutex);
|
||||
return ret;
|
||||
|
||||
err:
|
||||
drbg_dealloc_state(drbg);
|
||||
unlock:
|
||||
mutex_unlock(&drbg->drbg_mutex);
|
||||
return ret;
|
||||
|
||||
free_everything:
|
||||
mutex_unlock(&drbg->drbg_mutex);
|
||||
drbg_uninstantiate(drbg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1544,10 +1587,17 @@ err:
|
||||
*/
|
||||
static int drbg_uninstantiate(struct drbg_state *drbg)
|
||||
{
|
||||
spin_lock_bh(&drbg->drbg_lock);
|
||||
if (drbg->random_ready.func) {
|
||||
del_random_ready_callback(&drbg->random_ready);
|
||||
cancel_work_sync(&drbg->seed_work);
|
||||
crypto_free_rng(drbg->jent);
|
||||
drbg->jent = NULL;
|
||||
}
|
||||
|
||||
if (drbg->d_ops)
|
||||
drbg->d_ops->crypto_fini(drbg);
|
||||
drbg_dealloc_state(drbg);
|
||||
/* no scrubbing of test_data -- this shall survive an uninstantiate */
|
||||
spin_unlock_bh(&drbg->drbg_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1555,16 +1605,17 @@ static int drbg_uninstantiate(struct drbg_state *drbg)
|
||||
* Helper function for setting the test data in the DRBG
|
||||
*
|
||||
* @drbg DRBG state handle
|
||||
* @test_data test data to sets
|
||||
* @data test data
|
||||
* @len test data length
|
||||
*/
|
||||
static inline void drbg_set_testdata(struct drbg_state *drbg,
|
||||
struct drbg_test_data *test_data)
|
||||
static void drbg_kcapi_set_entropy(struct crypto_rng *tfm,
|
||||
const u8 *data, unsigned int len)
|
||||
{
|
||||
if (!test_data || !test_data->testentropy)
|
||||
return;
|
||||
spin_lock_bh(&drbg->drbg_lock);
|
||||
drbg->test_data = test_data;
|
||||
spin_unlock_bh(&drbg->drbg_lock);
|
||||
struct drbg_state *drbg = crypto_rng_ctx(tfm);
|
||||
|
||||
mutex_lock(&drbg->drbg_mutex);
|
||||
drbg_string_fill(&drbg->test_data, data, len);
|
||||
mutex_unlock(&drbg->drbg_mutex);
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
@ -1584,7 +1635,8 @@ static int drbg_init_hash_kernel(struct drbg_state *drbg)
|
||||
|
||||
tfm = crypto_alloc_shash(drbg->core->backend_cra_name, 0, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
pr_info("DRBG: could not allocate digest TFM handle\n");
|
||||
pr_info("DRBG: could not allocate digest TFM handle: %s\n",
|
||||
drbg->core->backend_cra_name);
|
||||
return PTR_ERR(tfm);
|
||||
}
|
||||
BUG_ON(drbg_blocklen(drbg) != crypto_shash_digestsize(tfm));
|
||||
@ -1635,7 +1687,8 @@ static int drbg_init_sym_kernel(struct drbg_state *drbg)
|
||||
|
||||
tfm = crypto_alloc_cipher(drbg->core->backend_cra_name, 0, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
pr_info("DRBG: could not allocate cipher TFM handle\n");
|
||||
pr_info("DRBG: could not allocate cipher TFM handle: %s\n",
|
||||
drbg->core->backend_cra_name);
|
||||
return PTR_ERR(tfm);
|
||||
}
|
||||
BUG_ON(drbg_blocklen(drbg) != crypto_cipher_blocksize(tfm));
|
||||
@ -1714,15 +1767,10 @@ static inline void drbg_convert_tfm_core(const char *cra_driver_name,
|
||||
static int drbg_kcapi_init(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct drbg_state *drbg = crypto_tfm_ctx(tfm);
|
||||
bool pr = false;
|
||||
int coreref = 0;
|
||||
|
||||
drbg_convert_tfm_core(crypto_tfm_alg_driver_name(tfm), &coreref, &pr);
|
||||
/*
|
||||
* when personalization string is needed, the caller must call reset
|
||||
* and provide the personalization string as seed information
|
||||
*/
|
||||
return drbg_instantiate(drbg, NULL, coreref, pr);
|
||||
mutex_init(&drbg->drbg_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void drbg_kcapi_cleanup(struct crypto_tfm *tfm)
|
||||
@ -1734,65 +1782,49 @@ static void drbg_kcapi_cleanup(struct crypto_tfm *tfm)
|
||||
* Generate random numbers invoked by the kernel crypto API:
|
||||
* The API of the kernel crypto API is extended as follows:
|
||||
*
|
||||
* If dlen is larger than zero, rdata is interpreted as the output buffer
|
||||
* where random data is to be stored.
|
||||
*
|
||||
* If dlen is zero, rdata is interpreted as a pointer to a struct drbg_gen
|
||||
* which holds the additional information string that is used for the
|
||||
* DRBG generation process. The output buffer that is to be used to store
|
||||
* data is also pointed to by struct drbg_gen.
|
||||
* src is additional input supplied to the RNG.
|
||||
* slen is the length of src.
|
||||
* dst is the output buffer where random data is to be stored.
|
||||
* dlen is the length of dst.
|
||||
*/
|
||||
static int drbg_kcapi_random(struct crypto_rng *tfm, u8 *rdata,
|
||||
unsigned int dlen)
|
||||
static int drbg_kcapi_random(struct crypto_rng *tfm,
|
||||
const u8 *src, unsigned int slen,
|
||||
u8 *dst, unsigned int dlen)
|
||||
{
|
||||
struct drbg_state *drbg = crypto_rng_ctx(tfm);
|
||||
if (0 < dlen) {
|
||||
return drbg_generate_long(drbg, rdata, dlen, NULL);
|
||||
} else {
|
||||
struct drbg_gen *data = (struct drbg_gen *)rdata;
|
||||
struct drbg_string addtl;
|
||||
/* catch NULL pointer */
|
||||
if (!data)
|
||||
return 0;
|
||||
drbg_set_testdata(drbg, data->test_data);
|
||||
struct drbg_string *addtl = NULL;
|
||||
struct drbg_string string;
|
||||
|
||||
if (slen) {
|
||||
/* linked list variable is now local to allow modification */
|
||||
drbg_string_fill(&addtl, data->addtl->buf, data->addtl->len);
|
||||
return drbg_generate_long(drbg, data->outbuf, data->outlen,
|
||||
&addtl);
|
||||
drbg_string_fill(&string, src, slen);
|
||||
addtl = &string;
|
||||
}
|
||||
|
||||
return drbg_generate_long(drbg, dst, dlen, addtl);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset the DRBG invoked by the kernel crypto API
|
||||
* The reset implies a full re-initialization of the DRBG. Similar to the
|
||||
* generate function of drbg_kcapi_random, this function extends the
|
||||
* kernel crypto API interface with struct drbg_gen
|
||||
* Seed the DRBG invoked by the kernel crypto API
|
||||
*/
|
||||
static int drbg_kcapi_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen)
|
||||
static int drbg_kcapi_seed(struct crypto_rng *tfm,
|
||||
const u8 *seed, unsigned int slen)
|
||||
{
|
||||
struct drbg_state *drbg = crypto_rng_ctx(tfm);
|
||||
struct crypto_tfm *tfm_base = crypto_rng_tfm(tfm);
|
||||
bool pr = false;
|
||||
struct drbg_string seed_string;
|
||||
struct drbg_string string;
|
||||
struct drbg_string *seed_string = NULL;
|
||||
int coreref = 0;
|
||||
|
||||
drbg_uninstantiate(drbg);
|
||||
drbg_convert_tfm_core(crypto_tfm_alg_driver_name(tfm_base), &coreref,
|
||||
&pr);
|
||||
if (0 < slen) {
|
||||
drbg_string_fill(&seed_string, seed, slen);
|
||||
return drbg_instantiate(drbg, &seed_string, coreref, pr);
|
||||
} else {
|
||||
struct drbg_gen *data = (struct drbg_gen *)seed;
|
||||
/* allow invocation of API call with NULL, 0 */
|
||||
if (!data)
|
||||
return drbg_instantiate(drbg, NULL, coreref, pr);
|
||||
drbg_set_testdata(drbg, data->test_data);
|
||||
/* linked list variable is now local to allow modification */
|
||||
drbg_string_fill(&seed_string, data->addtl->buf,
|
||||
data->addtl->len);
|
||||
return drbg_instantiate(drbg, &seed_string, coreref, pr);
|
||||
drbg_string_fill(&string, seed, slen);
|
||||
seed_string = &string;
|
||||
}
|
||||
|
||||
return drbg_instantiate(drbg, seed_string, coreref, pr);
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
@ -1811,7 +1843,6 @@ static int drbg_kcapi_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen)
|
||||
*/
|
||||
static inline int __init drbg_healthcheck_sanity(void)
|
||||
{
|
||||
#ifdef CONFIG_CRYPTO_FIPS
|
||||
int len = 0;
|
||||
#define OUTBUFLEN 16
|
||||
unsigned char buf[OUTBUFLEN];
|
||||
@ -1839,6 +1870,8 @@ static inline int __init drbg_healthcheck_sanity(void)
|
||||
if (!drbg)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&drbg->drbg_mutex);
|
||||
|
||||
/*
|
||||
* if the following tests fail, it is likely that there is a buffer
|
||||
* overflow as buf is much smaller than the requested or provided
|
||||
@ -1877,37 +1910,33 @@ static inline int __init drbg_healthcheck_sanity(void)
|
||||
outbuf:
|
||||
kzfree(drbg);
|
||||
return rc;
|
||||
#else /* CONFIG_CRYPTO_FIPS */
|
||||
return 0;
|
||||
#endif /* CONFIG_CRYPTO_FIPS */
|
||||
}
|
||||
|
||||
static struct crypto_alg drbg_algs[22];
|
||||
static struct rng_alg drbg_algs[22];
|
||||
|
||||
/*
|
||||
* Fill the array drbg_algs used to register the different DRBGs
|
||||
* with the kernel crypto API. To fill the array, the information
|
||||
* from drbg_cores[] is used.
|
||||
*/
|
||||
static inline void __init drbg_fill_array(struct crypto_alg *alg,
|
||||
static inline void __init drbg_fill_array(struct rng_alg *alg,
|
||||
const struct drbg_core *core, int pr)
|
||||
{
|
||||
int pos = 0;
|
||||
static int priority = 100;
|
||||
static int priority = 200;
|
||||
|
||||
memset(alg, 0, sizeof(struct crypto_alg));
|
||||
memcpy(alg->cra_name, "stdrng", 6);
|
||||
memcpy(alg->base.cra_name, "stdrng", 6);
|
||||
if (pr) {
|
||||
memcpy(alg->cra_driver_name, "drbg_pr_", 8);
|
||||
memcpy(alg->base.cra_driver_name, "drbg_pr_", 8);
|
||||
pos = 8;
|
||||
} else {
|
||||
memcpy(alg->cra_driver_name, "drbg_nopr_", 10);
|
||||
memcpy(alg->base.cra_driver_name, "drbg_nopr_", 10);
|
||||
pos = 10;
|
||||
}
|
||||
memcpy(alg->cra_driver_name + pos, core->cra_name,
|
||||
memcpy(alg->base.cra_driver_name + pos, core->cra_name,
|
||||
strlen(core->cra_name));
|
||||
|
||||
alg->cra_priority = priority;
|
||||
alg->base.cra_priority = priority;
|
||||
priority++;
|
||||
/*
|
||||
* If FIPS mode enabled, the selected DRBG shall have the
|
||||
@ -1915,17 +1944,16 @@ static inline void __init drbg_fill_array(struct crypto_alg *alg,
|
||||
* it is selected.
|
||||
*/
|
||||
if (fips_enabled)
|
||||
alg->cra_priority += 200;
|
||||
alg->base.cra_priority += 200;
|
||||
|
||||
alg->cra_flags = CRYPTO_ALG_TYPE_RNG;
|
||||
alg->cra_ctxsize = sizeof(struct drbg_state);
|
||||
alg->cra_type = &crypto_rng_type;
|
||||
alg->cra_module = THIS_MODULE;
|
||||
alg->cra_init = drbg_kcapi_init;
|
||||
alg->cra_exit = drbg_kcapi_cleanup;
|
||||
alg->cra_u.rng.rng_make_random = drbg_kcapi_random;
|
||||
alg->cra_u.rng.rng_reset = drbg_kcapi_reset;
|
||||
alg->cra_u.rng.seedsize = 0;
|
||||
alg->base.cra_ctxsize = sizeof(struct drbg_state);
|
||||
alg->base.cra_module = THIS_MODULE;
|
||||
alg->base.cra_init = drbg_kcapi_init;
|
||||
alg->base.cra_exit = drbg_kcapi_cleanup;
|
||||
alg->generate = drbg_kcapi_random;
|
||||
alg->seed = drbg_kcapi_seed;
|
||||
alg->set_ent = drbg_kcapi_set_entropy;
|
||||
alg->seedsize = 0;
|
||||
}
|
||||
|
||||
static int __init drbg_init(void)
|
||||
@ -1958,12 +1986,12 @@ static int __init drbg_init(void)
|
||||
drbg_fill_array(&drbg_algs[i], &drbg_cores[j], 1);
|
||||
for (j = 0; ARRAY_SIZE(drbg_cores) > j; j++, i++)
|
||||
drbg_fill_array(&drbg_algs[i], &drbg_cores[j], 0);
|
||||
return crypto_register_algs(drbg_algs, (ARRAY_SIZE(drbg_cores) * 2));
|
||||
return crypto_register_rngs(drbg_algs, (ARRAY_SIZE(drbg_cores) * 2));
|
||||
}
|
||||
|
||||
static void __exit drbg_exit(void)
|
||||
{
|
||||
crypto_unregister_algs(drbg_algs, (ARRAY_SIZE(drbg_cores) * 2));
|
||||
crypto_unregister_rngs(drbg_algs, (ARRAY_SIZE(drbg_cores) * 2));
|
||||
}
|
||||
|
||||
module_init(drbg_init);
|
||||
@ -1984,3 +2012,4 @@ MODULE_DESCRIPTION("NIST SP800-90A Deterministic Random Bit Generator (DRBG) "
|
||||
CRYPTO_DRBG_HASH_STRING
|
||||
CRYPTO_DRBG_HMAC_STRING
|
||||
CRYPTO_DRBG_CTR_STRING);
|
||||
MODULE_ALIAS_CRYPTO("stdrng");
|
||||
|
312
crypto/echainiv.c
Normal file
312
crypto/echainiv.c
Normal file
@ -0,0 +1,312 @@
|
||||
/*
|
||||
* echainiv: Encrypted Chain IV Generator
|
||||
*
|
||||
* This generator generates an IV based on a sequence number by xoring it
|
||||
* with a salt and then encrypting it with the same key as used to encrypt
|
||||
* the plain text. This algorithm requires that the block size be equal
|
||||
* to the IV size. It is mainly useful for CBC.
|
||||
*
|
||||
* This generator can only be used by algorithms where authentication
|
||||
* is performed after encryption (i.e., authenc).
|
||||
*
|
||||
* Copyright (c) 2015 Herbert Xu <herbert@gondor.apana.org.au>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <crypto/internal/geniv.h>
|
||||
#include <crypto/null.h>
|
||||
#include <crypto/rng.h>
|
||||
#include <crypto/scatterwalk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#define MAX_IV_SIZE 16
|
||||
|
||||
struct echainiv_ctx {
|
||||
/* aead_geniv_ctx must be first the element */
|
||||
struct aead_geniv_ctx geniv;
|
||||
struct crypto_blkcipher *null;
|
||||
u8 salt[] __attribute__ ((aligned(__alignof__(u32))));
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(u32 [MAX_IV_SIZE / sizeof(u32)], echainiv_iv);
|
||||
|
||||
/* We don't care if we get preempted and read/write IVs from the next CPU. */
|
||||
static void echainiv_read_iv(u8 *dst, unsigned size)
|
||||
{
|
||||
u32 *a = (u32 *)dst;
|
||||
u32 __percpu *b = echainiv_iv;
|
||||
|
||||
for (; size >= 4; size -= 4) {
|
||||
*a++ = this_cpu_read(*b);
|
||||
b++;
|
||||
}
|
||||
}
|
||||
|
||||
static void echainiv_write_iv(const u8 *src, unsigned size)
|
||||
{
|
||||
const u32 *a = (const u32 *)src;
|
||||
u32 __percpu *b = echainiv_iv;
|
||||
|
||||
for (; size >= 4; size -= 4) {
|
||||
this_cpu_write(*b, *a);
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
}
|
||||
|
||||
static void echainiv_encrypt_complete2(struct aead_request *req, int err)
|
||||
{
|
||||
struct aead_request *subreq = aead_request_ctx(req);
|
||||
struct crypto_aead *geniv;
|
||||
unsigned int ivsize;
|
||||
|
||||
if (err == -EINPROGRESS)
|
||||
return;
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
geniv = crypto_aead_reqtfm(req);
|
||||
ivsize = crypto_aead_ivsize(geniv);
|
||||
|
||||
echainiv_write_iv(subreq->iv, ivsize);
|
||||
|
||||
if (req->iv != subreq->iv)
|
||||
memcpy(req->iv, subreq->iv, ivsize);
|
||||
|
||||
out:
|
||||
if (req->iv != subreq->iv)
|
||||
kzfree(subreq->iv);
|
||||
}
|
||||
|
||||
static void echainiv_encrypt_complete(struct crypto_async_request *base,
|
||||
int err)
|
||||
{
|
||||
struct aead_request *req = base->data;
|
||||
|
||||
echainiv_encrypt_complete2(req, err);
|
||||
aead_request_complete(req, err);
|
||||
}
|
||||
|
||||
static int echainiv_encrypt(struct aead_request *req)
|
||||
{
|
||||
struct crypto_aead *geniv = crypto_aead_reqtfm(req);
|
||||
struct echainiv_ctx *ctx = crypto_aead_ctx(geniv);
|
||||
struct aead_request *subreq = aead_request_ctx(req);
|
||||
crypto_completion_t compl;
|
||||
void *data;
|
||||
u8 *info;
|
||||
unsigned int ivsize = crypto_aead_ivsize(geniv);
|
||||
int err;
|
||||
|
||||
if (req->cryptlen < ivsize)
|
||||
return -EINVAL;
|
||||
|
||||
aead_request_set_tfm(subreq, ctx->geniv.child);
|
||||
|
||||
compl = echainiv_encrypt_complete;
|
||||
data = req;
|
||||
info = req->iv;
|
||||
|
||||
if (req->src != req->dst) {
|
||||
struct blkcipher_desc desc = {
|
||||
.tfm = ctx->null,
|
||||
};
|
||||
|
||||
err = crypto_blkcipher_encrypt(
|
||||
&desc, req->dst, req->src,
|
||||
req->assoclen + req->cryptlen);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (unlikely(!IS_ALIGNED((unsigned long)info,
|
||||
crypto_aead_alignmask(geniv) + 1))) {
|
||||
info = kmalloc(ivsize, req->base.flags &
|
||||
CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL:
|
||||
GFP_ATOMIC);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(info, req->iv, ivsize);
|
||||
}
|
||||
|
||||
aead_request_set_callback(subreq, req->base.flags, compl, data);
|
||||
aead_request_set_crypt(subreq, req->dst, req->dst,
|
||||
req->cryptlen - ivsize, info);
|
||||
aead_request_set_ad(subreq, req->assoclen + ivsize);
|
||||
|
||||
crypto_xor(info, ctx->salt, ivsize);
|
||||
scatterwalk_map_and_copy(info, req->dst, req->assoclen, ivsize, 1);
|
||||
echainiv_read_iv(info, ivsize);
|
||||
|
||||
err = crypto_aead_encrypt(subreq);
|
||||
echainiv_encrypt_complete2(req, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int echainiv_decrypt(struct aead_request *req)
|
||||
{
|
||||
struct crypto_aead *geniv = crypto_aead_reqtfm(req);
|
||||
struct echainiv_ctx *ctx = crypto_aead_ctx(geniv);
|
||||
struct aead_request *subreq = aead_request_ctx(req);
|
||||
crypto_completion_t compl;
|
||||
void *data;
|
||||
unsigned int ivsize = crypto_aead_ivsize(geniv);
|
||||
|
||||
if (req->cryptlen < ivsize + crypto_aead_authsize(geniv))
|
||||
return -EINVAL;
|
||||
|
||||
aead_request_set_tfm(subreq, ctx->geniv.child);
|
||||
|
||||
compl = req->base.complete;
|
||||
data = req->base.data;
|
||||
|
||||
aead_request_set_callback(subreq, req->base.flags, compl, data);
|
||||
aead_request_set_crypt(subreq, req->src, req->dst,
|
||||
req->cryptlen - ivsize, req->iv);
|
||||
aead_request_set_ad(subreq, req->assoclen + ivsize);
|
||||
|
||||
scatterwalk_map_and_copy(req->iv, req->src, req->assoclen, ivsize, 0);
|
||||
if (req->src != req->dst)
|
||||
scatterwalk_map_and_copy(req->iv, req->dst,
|
||||
req->assoclen, ivsize, 1);
|
||||
|
||||
return crypto_aead_decrypt(subreq);
|
||||
}
|
||||
|
||||
static int echainiv_init(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct crypto_aead *geniv = __crypto_aead_cast(tfm);
|
||||
struct echainiv_ctx *ctx = crypto_aead_ctx(geniv);
|
||||
int err;
|
||||
|
||||
spin_lock_init(&ctx->geniv.lock);
|
||||
|
||||
crypto_aead_set_reqsize(geniv, sizeof(struct aead_request));
|
||||
|
||||
err = crypto_get_default_rng();
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = crypto_rng_get_bytes(crypto_default_rng, ctx->salt,
|
||||
crypto_aead_ivsize(geniv));
|
||||
crypto_put_default_rng();
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
ctx->null = crypto_get_default_null_skcipher();
|
||||
err = PTR_ERR(ctx->null);
|
||||
if (IS_ERR(ctx->null))
|
||||
goto out;
|
||||
|
||||
err = aead_geniv_init(tfm);
|
||||
if (err)
|
||||
goto drop_null;
|
||||
|
||||
ctx->geniv.child = geniv->child;
|
||||
geniv->child = geniv;
|
||||
|
||||
out:
|
||||
return err;
|
||||
|
||||
drop_null:
|
||||
crypto_put_default_null_skcipher();
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void echainiv_exit(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct echainiv_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
|
||||
crypto_free_aead(ctx->geniv.child);
|
||||
crypto_put_default_null_skcipher();
|
||||
}
|
||||
|
||||
static int echainiv_aead_create(struct crypto_template *tmpl,
|
||||
struct rtattr **tb)
|
||||
{
|
||||
struct aead_instance *inst;
|
||||
struct crypto_aead_spawn *spawn;
|
||||
struct aead_alg *alg;
|
||||
int err;
|
||||
|
||||
inst = aead_geniv_alloc(tmpl, tb, 0, 0);
|
||||
|
||||
if (IS_ERR(inst))
|
||||
return PTR_ERR(inst);
|
||||
|
||||
spawn = aead_instance_ctx(inst);
|
||||
alg = crypto_spawn_aead_alg(spawn);
|
||||
|
||||
if (alg->base.cra_aead.encrypt)
|
||||
goto done;
|
||||
|
||||
err = -EINVAL;
|
||||
if (inst->alg.ivsize & (sizeof(u32) - 1) ||
|
||||
inst->alg.ivsize > MAX_IV_SIZE)
|
||||
goto free_inst;
|
||||
|
||||
inst->alg.encrypt = echainiv_encrypt;
|
||||
inst->alg.decrypt = echainiv_decrypt;
|
||||
|
||||
inst->alg.base.cra_init = echainiv_init;
|
||||
inst->alg.base.cra_exit = echainiv_exit;
|
||||
|
||||
inst->alg.base.cra_alignmask |= __alignof__(u32) - 1;
|
||||
inst->alg.base.cra_ctxsize = sizeof(struct echainiv_ctx);
|
||||
inst->alg.base.cra_ctxsize += inst->alg.ivsize;
|
||||
|
||||
done:
|
||||
err = aead_register_instance(tmpl, inst);
|
||||
if (err)
|
||||
goto free_inst;
|
||||
|
||||
out:
|
||||
return err;
|
||||
|
||||
free_inst:
|
||||
aead_geniv_free(inst);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void echainiv_free(struct crypto_instance *inst)
|
||||
{
|
||||
aead_geniv_free(aead_instance(inst));
|
||||
}
|
||||
|
||||
static struct crypto_template echainiv_tmpl = {
|
||||
.name = "echainiv",
|
||||
.create = echainiv_aead_create,
|
||||
.free = echainiv_free,
|
||||
.module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init echainiv_module_init(void)
|
||||
{
|
||||
return crypto_register_template(&echainiv_tmpl);
|
||||
}
|
||||
|
||||
static void __exit echainiv_module_exit(void)
|
||||
{
|
||||
crypto_unregister_template(&echainiv_tmpl);
|
||||
}
|
||||
|
||||
module_init(echainiv_module_init);
|
||||
module_exit(echainiv_module_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Encrypted Chain IV Generator");
|
||||
MODULE_ALIAS_CRYPTO("echainiv");
|
@ -146,35 +146,13 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int eseqiv_givencrypt_first(struct skcipher_givcrypt_request *req)
|
||||
{
|
||||
struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req);
|
||||
struct eseqiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
|
||||
int err = 0;
|
||||
|
||||
spin_lock_bh(&ctx->lock);
|
||||
if (crypto_ablkcipher_crt(geniv)->givencrypt != eseqiv_givencrypt_first)
|
||||
goto unlock;
|
||||
|
||||
crypto_ablkcipher_crt(geniv)->givencrypt = eseqiv_givencrypt;
|
||||
err = crypto_rng_get_bytes(crypto_default_rng, ctx->salt,
|
||||
crypto_ablkcipher_ivsize(geniv));
|
||||
|
||||
unlock:
|
||||
spin_unlock_bh(&ctx->lock);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return eseqiv_givencrypt(req);
|
||||
}
|
||||
|
||||
static int eseqiv_init(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct crypto_ablkcipher *geniv = __crypto_ablkcipher_cast(tfm);
|
||||
struct eseqiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
|
||||
unsigned long alignmask;
|
||||
unsigned int reqsize;
|
||||
int err;
|
||||
|
||||
spin_lock_init(&ctx->lock);
|
||||
|
||||
@ -198,7 +176,15 @@ static int eseqiv_init(struct crypto_tfm *tfm)
|
||||
tfm->crt_ablkcipher.reqsize = reqsize +
|
||||
sizeof(struct ablkcipher_request);
|
||||
|
||||
return skcipher_geniv_init(tfm);
|
||||
err = 0;
|
||||
if (!crypto_get_default_rng()) {
|
||||
crypto_ablkcipher_crt(geniv)->givencrypt = eseqiv_givencrypt;
|
||||
err = crypto_rng_get_bytes(crypto_default_rng, ctx->salt,
|
||||
crypto_ablkcipher_ivsize(geniv));
|
||||
crypto_put_default_rng();
|
||||
}
|
||||
|
||||
return err ?: skcipher_geniv_init(tfm);
|
||||
}
|
||||
|
||||
static struct crypto_template eseqiv_tmpl;
|
||||
@ -208,20 +194,14 @@ static struct crypto_instance *eseqiv_alloc(struct rtattr **tb)
|
||||
struct crypto_instance *inst;
|
||||
int err;
|
||||
|
||||
err = crypto_get_default_rng();
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
inst = skcipher_geniv_alloc(&eseqiv_tmpl, tb, 0, 0);
|
||||
if (IS_ERR(inst))
|
||||
goto put_rng;
|
||||
goto out;
|
||||
|
||||
err = -EINVAL;
|
||||
if (inst->alg.cra_ablkcipher.ivsize != inst->alg.cra_blocksize)
|
||||
goto free_inst;
|
||||
|
||||
inst->alg.cra_ablkcipher.givencrypt = eseqiv_givencrypt_first;
|
||||
|
||||
inst->alg.cra_init = eseqiv_init;
|
||||
inst->alg.cra_exit = skcipher_geniv_exit;
|
||||
|
||||
@ -234,21 +214,13 @@ out:
|
||||
free_inst:
|
||||
skcipher_geniv_free(inst);
|
||||
inst = ERR_PTR(err);
|
||||
put_rng:
|
||||
crypto_put_default_rng();
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void eseqiv_free(struct crypto_instance *inst)
|
||||
{
|
||||
skcipher_geniv_free(inst);
|
||||
crypto_put_default_rng();
|
||||
}
|
||||
|
||||
static struct crypto_template eseqiv_tmpl = {
|
||||
.name = "eseqiv",
|
||||
.alloc = eseqiv_alloc,
|
||||
.free = eseqiv_free,
|
||||
.free = skcipher_geniv_free,
|
||||
.module = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
@ -10,7 +10,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include <linux/export.h>
|
||||
#include <linux/fips.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sysctl.h>
|
||||
|
||||
int fips_enabled;
|
||||
EXPORT_SYMBOL_GPL(fips_enabled);
|
||||
@ -25,3 +30,49 @@ static int fips_enable(char *str)
|
||||
}
|
||||
|
||||
__setup("fips=", fips_enable);
|
||||
|
||||
static struct ctl_table crypto_sysctl_table[] = {
|
||||
{
|
||||
.procname = "fips_enabled",
|
||||
.data = &fips_enabled,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0444,
|
||||
.proc_handler = proc_dointvec
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct ctl_table crypto_dir_table[] = {
|
||||
{
|
||||
.procname = "crypto",
|
||||
.mode = 0555,
|
||||
.child = crypto_sysctl_table
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct ctl_table_header *crypto_sysctls;
|
||||
|
||||
static void crypto_proc_fips_init(void)
|
||||
{
|
||||
crypto_sysctls = register_sysctl_table(crypto_dir_table);
|
||||
}
|
||||
|
||||
static void crypto_proc_fips_exit(void)
|
||||
{
|
||||
unregister_sysctl_table(crypto_sysctls);
|
||||
}
|
||||
|
||||
static int __init fips_init(void)
|
||||
{
|
||||
crypto_proc_fips_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit fips_exit(void)
|
||||
{
|
||||
crypto_proc_fips_exit();
|
||||
}
|
||||
|
||||
module_init(fips_init);
|
||||
module_exit(fips_exit);
|
||||
|
944
crypto/gcm.c
944
crypto/gcm.c
File diff suppressed because it is too large
Load Diff
@ -25,7 +25,6 @@
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fips.h>
|
||||
|
||||
/* Crypto notification events. */
|
||||
enum {
|
||||
@ -103,6 +102,8 @@ int crypto_register_notifier(struct notifier_block *nb);
|
||||
int crypto_unregister_notifier(struct notifier_block *nb);
|
||||
int crypto_probing_notify(unsigned long val, void *v);
|
||||
|
||||
unsigned int crypto_alg_extsize(struct crypto_alg *alg);
|
||||
|
||||
static inline struct crypto_alg *crypto_alg_get(struct crypto_alg *alg)
|
||||
{
|
||||
atomic_inc(&alg->cra_refcnt);
|
||||
|
928
crypto/jitterentropy.c
Normal file
928
crypto/jitterentropy.c
Normal file
@ -0,0 +1,928 @@
|
||||
/*
|
||||
* Non-physical true random number generator based on timing jitter.
|
||||
*
|
||||
* Copyright Stephan Mueller <smueller@chronox.de>, 2014
|
||||
*
|
||||
* Design
|
||||
* ======
|
||||
*
|
||||
* See http://www.chronox.de/jent.html
|
||||
*
|
||||
* License
|
||||
* =======
|
||||
*
|
||||
* 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, and the entire permission notice in its entirety,
|
||||
* including the disclaimer of warranties.
|
||||
* 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.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior
|
||||
* written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this product may be distributed under the terms of
|
||||
* the GNU General Public License, in which case the provisions of the GPL2 are
|
||||
* required INSTEAD OF the above restrictions. (This clause is
|
||||
* necessary due to a potential bad interaction between the GPL and
|
||||
* the restrictions contained in a BSD-style copyright.)
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
|
||||
* WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This Jitterentropy RNG is based on the jitterentropy library
|
||||
* version 1.1.0 provided at http://www.chronox.de/jent.html
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fips.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <crypto/internal/rng.h>
|
||||
|
||||
/* The entropy pool */
|
||||
struct rand_data {
|
||||
/* all data values that are vital to maintain the security
|
||||
* of the RNG are marked as SENSITIVE. A user must not
|
||||
* access that information while the RNG executes its loops to
|
||||
* calculate the next random value. */
|
||||
__u64 data; /* SENSITIVE Actual random number */
|
||||
__u64 old_data; /* SENSITIVE Previous random number */
|
||||
__u64 prev_time; /* SENSITIVE Previous time stamp */
|
||||
#define DATA_SIZE_BITS ((sizeof(__u64)) * 8)
|
||||
__u64 last_delta; /* SENSITIVE stuck test */
|
||||
__s64 last_delta2; /* SENSITIVE stuck test */
|
||||
unsigned int stuck:1; /* Time measurement stuck */
|
||||
unsigned int osr; /* Oversample rate */
|
||||
unsigned int stir:1; /* Post-processing stirring */
|
||||
unsigned int disable_unbias:1; /* Deactivate Von-Neuman unbias */
|
||||
#define JENT_MEMORY_BLOCKS 64
|
||||
#define JENT_MEMORY_BLOCKSIZE 32
|
||||
#define JENT_MEMORY_ACCESSLOOPS 128
|
||||
#define JENT_MEMORY_SIZE (JENT_MEMORY_BLOCKS*JENT_MEMORY_BLOCKSIZE)
|
||||
unsigned char *mem; /* Memory access location with size of
|
||||
* memblocks * memblocksize */
|
||||
unsigned int memlocation; /* Pointer to byte in *mem */
|
||||
unsigned int memblocks; /* Number of memory blocks in *mem */
|
||||
unsigned int memblocksize; /* Size of one memory block in bytes */
|
||||
unsigned int memaccessloops; /* Number of memory accesses per random
|
||||
* bit generation */
|
||||
};
|
||||
|
||||
/* Flags that can be used to initialize the RNG */
|
||||
#define JENT_DISABLE_STIR (1<<0) /* Disable stirring the entropy pool */
|
||||
#define JENT_DISABLE_UNBIAS (1<<1) /* Disable the Von-Neuman Unbiaser */
|
||||
#define JENT_DISABLE_MEMORY_ACCESS (1<<2) /* Disable memory access for more
|
||||
* entropy, saves MEMORY_SIZE RAM for
|
||||
* entropy collector */
|
||||
|
||||
#define DRIVER_NAME "jitterentropy"
|
||||
|
||||
/* -- error codes for init function -- */
|
||||
#define JENT_ENOTIME 1 /* Timer service not available */
|
||||
#define JENT_ECOARSETIME 2 /* Timer too coarse for RNG */
|
||||
#define JENT_ENOMONOTONIC 3 /* Timer is not monotonic increasing */
|
||||
#define JENT_EMINVARIATION 4 /* Timer variations too small for RNG */
|
||||
#define JENT_EVARVAR 5 /* Timer does not produce variations of
|
||||
* variations (2nd derivation of time is
|
||||
* zero). */
|
||||
#define JENT_EMINVARVAR 6 /* Timer variations of variations is tooi
|
||||
* small. */
|
||||
|
||||
/***************************************************************************
|
||||
* Helper functions
|
||||
***************************************************************************/
|
||||
|
||||
static inline void jent_get_nstime(__u64 *out)
|
||||
{
|
||||
struct timespec ts;
|
||||
__u64 tmp = 0;
|
||||
|
||||
tmp = random_get_entropy();
|
||||
|
||||
/*
|
||||
* If random_get_entropy does not return a value (which is possible on,
|
||||
* for example, MIPS), invoke __getnstimeofday
|
||||
* hoping that there are timers we can work with.
|
||||
*
|
||||
* The list of available timers can be obtained from
|
||||
* /sys/devices/system/clocksource/clocksource0/available_clocksource
|
||||
* and are registered with clocksource_register()
|
||||
*/
|
||||
if ((0 == tmp) &&
|
||||
(0 == __getnstimeofday(&ts))) {
|
||||
tmp = ts.tv_sec;
|
||||
tmp = tmp << 32;
|
||||
tmp = tmp | ts.tv_nsec;
|
||||
}
|
||||
|
||||
*out = tmp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update of the loop count used for the next round of
|
||||
* an entropy collection.
|
||||
*
|
||||
* Input:
|
||||
* @ec entropy collector struct -- may be NULL
|
||||
* @bits is the number of low bits of the timer to consider
|
||||
* @min is the number of bits we shift the timer value to the right at
|
||||
* the end to make sure we have a guaranteed minimum value
|
||||
*
|
||||
* @return Newly calculated loop counter
|
||||
*/
|
||||
static __u64 jent_loop_shuffle(struct rand_data *ec,
|
||||
unsigned int bits, unsigned int min)
|
||||
{
|
||||
__u64 time = 0;
|
||||
__u64 shuffle = 0;
|
||||
unsigned int i = 0;
|
||||
unsigned int mask = (1<<bits) - 1;
|
||||
|
||||
jent_get_nstime(&time);
|
||||
/*
|
||||
* mix the current state of the random number into the shuffle
|
||||
* calculation to balance that shuffle a bit more
|
||||
*/
|
||||
if (ec)
|
||||
time ^= ec->data;
|
||||
/*
|
||||
* we fold the time value as much as possible to ensure that as many
|
||||
* bits of the time stamp are included as possible
|
||||
*/
|
||||
for (i = 0; (DATA_SIZE_BITS / bits) > i; i++) {
|
||||
shuffle ^= time & mask;
|
||||
time = time >> bits;
|
||||
}
|
||||
|
||||
/*
|
||||
* We add a lower boundary value to ensure we have a minimum
|
||||
* RNG loop count.
|
||||
*/
|
||||
return (shuffle + (1<<min));
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
* Noise sources
|
||||
***************************************************************************/
|
||||
|
||||
/*
|
||||
* The disabling of the optimizations is performed as documented and assessed
|
||||
* thoroughly in http://www.chronox.de/jent.html. However, instead of disabling
|
||||
* the optimization of the entire C file, only the main functions the jitter is
|
||||
* measured for are not optimized. These functions include the noise sources as
|
||||
* well as the main functions triggering the noise sources. As the time
|
||||
* measurement is done from one invocation of the jitter noise source to the
|
||||
* next, even the execution jitter of the code invoking the noise sources
|
||||
* contribute to the overall randomness as well. The behavior of the RNG and the
|
||||
* statistical characteristics when only the mentioned functions are not
|
||||
* optimized is almost equal to the a completely non-optimized RNG compilation
|
||||
* as tested with the test tools provided at the initially mentioned web site.
|
||||
*/
|
||||
|
||||
/**
|
||||
* CPU Jitter noise source -- this is the noise source based on the CPU
|
||||
* execution time jitter
|
||||
*
|
||||
* This function folds the time into one bit units by iterating
|
||||
* through the DATA_SIZE_BITS bit time value as follows: assume our time value
|
||||
* is 0xabcd
|
||||
* 1st loop, 1st shift generates 0xd000
|
||||
* 1st loop, 2nd shift generates 0x000d
|
||||
* 2nd loop, 1st shift generates 0xcd00
|
||||
* 2nd loop, 2nd shift generates 0x000c
|
||||
* 3rd loop, 1st shift generates 0xbcd0
|
||||
* 3rd loop, 2nd shift generates 0x000b
|
||||
* 4th loop, 1st shift generates 0xabcd
|
||||
* 4th loop, 2nd shift generates 0x000a
|
||||
* Now, the values at the end of the 2nd shifts are XORed together.
|
||||
*
|
||||
* The code is deliberately inefficient and shall stay that way. This function
|
||||
* is the root cause why the code shall be compiled without optimization. This
|
||||
* function not only acts as folding operation, but this function's execution
|
||||
* is used to measure the CPU execution time jitter. Any change to the loop in
|
||||
* this function implies that careful retesting must be done.
|
||||
*
|
||||
* Input:
|
||||
* @ec entropy collector struct -- may be NULL
|
||||
* @time time stamp to be folded
|
||||
* @loop_cnt if a value not equal to 0 is set, use the given value as number of
|
||||
* loops to perform the folding
|
||||
*
|
||||
* Output:
|
||||
* @folded result of folding operation
|
||||
*
|
||||
* @return Number of loops the folding operation is performed
|
||||
*/
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("-O0")
|
||||
static __u64 jent_fold_time(struct rand_data *ec, __u64 time,
|
||||
__u64 *folded, __u64 loop_cnt)
|
||||
{
|
||||
unsigned int i;
|
||||
__u64 j = 0;
|
||||
__u64 new = 0;
|
||||
#define MAX_FOLD_LOOP_BIT 4
|
||||
#define MIN_FOLD_LOOP_BIT 0
|
||||
__u64 fold_loop_cnt =
|
||||
jent_loop_shuffle(ec, MAX_FOLD_LOOP_BIT, MIN_FOLD_LOOP_BIT);
|
||||
|
||||
/*
|
||||
* testing purposes -- allow test app to set the counter, not
|
||||
* needed during runtime
|
||||
*/
|
||||
if (loop_cnt)
|
||||
fold_loop_cnt = loop_cnt;
|
||||
for (j = 0; j < fold_loop_cnt; j++) {
|
||||
new = 0;
|
||||
for (i = 1; (DATA_SIZE_BITS) >= i; i++) {
|
||||
__u64 tmp = time << (DATA_SIZE_BITS - i);
|
||||
|
||||
tmp = tmp >> (DATA_SIZE_BITS - 1);
|
||||
new ^= tmp;
|
||||
}
|
||||
}
|
||||
*folded = new;
|
||||
return fold_loop_cnt;
|
||||
}
|
||||
#pragma GCC pop_options
|
||||
|
||||
/**
|
||||
* Memory Access noise source -- this is a noise source based on variations in
|
||||
* memory access times
|
||||
*
|
||||
* This function performs memory accesses which will add to the timing
|
||||
* variations due to an unknown amount of CPU wait states that need to be
|
||||
* added when accessing memory. The memory size should be larger than the L1
|
||||
* caches as outlined in the documentation and the associated testing.
|
||||
*
|
||||
* The L1 cache has a very high bandwidth, albeit its access rate is usually
|
||||
* slower than accessing CPU registers. Therefore, L1 accesses only add minimal
|
||||
* variations as the CPU has hardly to wait. Starting with L2, significant
|
||||
* variations are added because L2 typically does not belong to the CPU any more
|
||||
* and therefore a wider range of CPU wait states is necessary for accesses.
|
||||
* L3 and real memory accesses have even a wider range of wait states. However,
|
||||
* to reliably access either L3 or memory, the ec->mem memory must be quite
|
||||
* large which is usually not desirable.
|
||||
*
|
||||
* Input:
|
||||
* @ec Reference to the entropy collector with the memory access data -- if
|
||||
* the reference to the memory block to be accessed is NULL, this noise
|
||||
* source is disabled
|
||||
* @loop_cnt if a value not equal to 0 is set, use the given value as number of
|
||||
* loops to perform the folding
|
||||
*
|
||||
* @return Number of memory access operations
|
||||
*/
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("-O0")
|
||||
static unsigned int jent_memaccess(struct rand_data *ec, __u64 loop_cnt)
|
||||
{
|
||||
unsigned char *tmpval = NULL;
|
||||
unsigned int wrap = 0;
|
||||
__u64 i = 0;
|
||||
#define MAX_ACC_LOOP_BIT 7
|
||||
#define MIN_ACC_LOOP_BIT 0
|
||||
__u64 acc_loop_cnt =
|
||||
jent_loop_shuffle(ec, MAX_ACC_LOOP_BIT, MIN_ACC_LOOP_BIT);
|
||||
|
||||
if (NULL == ec || NULL == ec->mem)
|
||||
return 0;
|
||||
wrap = ec->memblocksize * ec->memblocks;
|
||||
|
||||
/*
|
||||
* testing purposes -- allow test app to set the counter, not
|
||||
* needed during runtime
|
||||
*/
|
||||
if (loop_cnt)
|
||||
acc_loop_cnt = loop_cnt;
|
||||
|
||||
for (i = 0; i < (ec->memaccessloops + acc_loop_cnt); i++) {
|
||||
tmpval = ec->mem + ec->memlocation;
|
||||
/*
|
||||
* memory access: just add 1 to one byte,
|
||||
* wrap at 255 -- memory access implies read
|
||||
* from and write to memory location
|
||||
*/
|
||||
*tmpval = (*tmpval + 1) & 0xff;
|
||||
/*
|
||||
* Addition of memblocksize - 1 to pointer
|
||||
* with wrap around logic to ensure that every
|
||||
* memory location is hit evenly
|
||||
*/
|
||||
ec->memlocation = ec->memlocation + ec->memblocksize - 1;
|
||||
ec->memlocation = ec->memlocation % wrap;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
#pragma GCC pop_options
|
||||
|
||||
/***************************************************************************
|
||||
* Start of entropy processing logic
|
||||
***************************************************************************/
|
||||
|
||||
/**
|
||||
* Stuck test by checking the:
|
||||
* 1st derivation of the jitter measurement (time delta)
|
||||
* 2nd derivation of the jitter measurement (delta of time deltas)
|
||||
* 3rd derivation of the jitter measurement (delta of delta of time deltas)
|
||||
*
|
||||
* All values must always be non-zero.
|
||||
*
|
||||
* Input:
|
||||
* @ec Reference to entropy collector
|
||||
* @current_delta Jitter time delta
|
||||
*
|
||||
* @return
|
||||
* 0 jitter measurement not stuck (good bit)
|
||||
* 1 jitter measurement stuck (reject bit)
|
||||
*/
|
||||
static void jent_stuck(struct rand_data *ec, __u64 current_delta)
|
||||
{
|
||||
__s64 delta2 = ec->last_delta - current_delta;
|
||||
__s64 delta3 = delta2 - ec->last_delta2;
|
||||
|
||||
ec->last_delta = current_delta;
|
||||
ec->last_delta2 = delta2;
|
||||
|
||||
if (!current_delta || !delta2 || !delta3)
|
||||
ec->stuck = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the heart of the entropy generation: calculate time deltas and
|
||||
* use the CPU jitter in the time deltas. The jitter is folded into one
|
||||
* bit. You can call this function the "random bit generator" as it
|
||||
* produces one random bit per invocation.
|
||||
*
|
||||
* WARNING: ensure that ->prev_time is primed before using the output
|
||||
* of this function! This can be done by calling this function
|
||||
* and not using its result.
|
||||
*
|
||||
* Input:
|
||||
* @entropy_collector Reference to entropy collector
|
||||
*
|
||||
* @return One random bit
|
||||
*/
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("-O0")
|
||||
static __u64 jent_measure_jitter(struct rand_data *ec)
|
||||
{
|
||||
__u64 time = 0;
|
||||
__u64 data = 0;
|
||||
__u64 current_delta = 0;
|
||||
|
||||
/* Invoke one noise source before time measurement to add variations */
|
||||
jent_memaccess(ec, 0);
|
||||
|
||||
/*
|
||||
* Get time stamp and calculate time delta to previous
|
||||
* invocation to measure the timing variations
|
||||
*/
|
||||
jent_get_nstime(&time);
|
||||
current_delta = time - ec->prev_time;
|
||||
ec->prev_time = time;
|
||||
|
||||
/* Now call the next noise sources which also folds the data */
|
||||
jent_fold_time(ec, current_delta, &data, 0);
|
||||
|
||||
/*
|
||||
* Check whether we have a stuck measurement. The enforcement
|
||||
* is performed after the stuck value has been mixed into the
|
||||
* entropy pool.
|
||||
*/
|
||||
jent_stuck(ec, current_delta);
|
||||
|
||||
return data;
|
||||
}
|
||||
#pragma GCC pop_options
|
||||
|
||||
/**
|
||||
* Von Neuman unbias as explained in RFC 4086 section 4.2. As shown in the
|
||||
* documentation of that RNG, the bits from jent_measure_jitter are considered
|
||||
* independent which implies that the Von Neuman unbias operation is applicable.
|
||||
* A proof of the Von-Neumann unbias operation to remove skews is given in the
|
||||
* document "A proposal for: Functionality classes for random number
|
||||
* generators", version 2.0 by Werner Schindler, section 5.4.1.
|
||||
*
|
||||
* Input:
|
||||
* @entropy_collector Reference to entropy collector
|
||||
*
|
||||
* @return One random bit
|
||||
*/
|
||||
static __u64 jent_unbiased_bit(struct rand_data *entropy_collector)
|
||||
{
|
||||
do {
|
||||
__u64 a = jent_measure_jitter(entropy_collector);
|
||||
__u64 b = jent_measure_jitter(entropy_collector);
|
||||
|
||||
if (a == b)
|
||||
continue;
|
||||
if (1 == a)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
} while (1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffle the pool a bit by mixing some value with a bijective function (XOR)
|
||||
* into the pool.
|
||||
*
|
||||
* The function generates a mixer value that depends on the bits set and the
|
||||
* location of the set bits in the random number generated by the entropy
|
||||
* source. Therefore, based on the generated random number, this mixer value
|
||||
* can have 2**64 different values. That mixer value is initialized with the
|
||||
* first two SHA-1 constants. After obtaining the mixer value, it is XORed into
|
||||
* the random number.
|
||||
*
|
||||
* The mixer value is not assumed to contain any entropy. But due to the XOR
|
||||
* operation, it can also not destroy any entropy present in the entropy pool.
|
||||
*
|
||||
* Input:
|
||||
* @entropy_collector Reference to entropy collector
|
||||
*/
|
||||
static void jent_stir_pool(struct rand_data *entropy_collector)
|
||||
{
|
||||
/*
|
||||
* to shut up GCC on 32 bit, we have to initialize the 64 variable
|
||||
* with two 32 bit variables
|
||||
*/
|
||||
union c {
|
||||
__u64 u64;
|
||||
__u32 u32[2];
|
||||
};
|
||||
/*
|
||||
* This constant is derived from the first two 32 bit initialization
|
||||
* vectors of SHA-1 as defined in FIPS 180-4 section 5.3.1
|
||||
*/
|
||||
union c constant;
|
||||
/*
|
||||
* The start value of the mixer variable is derived from the third
|
||||
* and fourth 32 bit initialization vector of SHA-1 as defined in
|
||||
* FIPS 180-4 section 5.3.1
|
||||
*/
|
||||
union c mixer;
|
||||
unsigned int i = 0;
|
||||
|
||||
/*
|
||||
* Store the SHA-1 constants in reverse order to make up the 64 bit
|
||||
* value -- this applies to a little endian system, on a big endian
|
||||
* system, it reverses as expected. But this really does not matter
|
||||
* as we do not rely on the specific numbers. We just pick the SHA-1
|
||||
* constants as they have a good mix of bit set and unset.
|
||||
*/
|
||||
constant.u32[1] = 0x67452301;
|
||||
constant.u32[0] = 0xefcdab89;
|
||||
mixer.u32[1] = 0x98badcfe;
|
||||
mixer.u32[0] = 0x10325476;
|
||||
|
||||
for (i = 0; i < DATA_SIZE_BITS; i++) {
|
||||
/*
|
||||
* get the i-th bit of the input random number and only XOR
|
||||
* the constant into the mixer value when that bit is set
|
||||
*/
|
||||
if ((entropy_collector->data >> i) & 1)
|
||||
mixer.u64 ^= constant.u64;
|
||||
mixer.u64 = rol64(mixer.u64, 1);
|
||||
}
|
||||
entropy_collector->data ^= mixer.u64;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generator of one 64 bit random number
|
||||
* Function fills rand_data->data
|
||||
*
|
||||
* Input:
|
||||
* @ec Reference to entropy collector
|
||||
*/
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("-O0")
|
||||
static void jent_gen_entropy(struct rand_data *ec)
|
||||
{
|
||||
unsigned int k = 0;
|
||||
|
||||
/* priming of the ->prev_time value */
|
||||
jent_measure_jitter(ec);
|
||||
|
||||
while (1) {
|
||||
__u64 data = 0;
|
||||
|
||||
if (ec->disable_unbias == 1)
|
||||
data = jent_measure_jitter(ec);
|
||||
else
|
||||
data = jent_unbiased_bit(ec);
|
||||
|
||||
/* enforcement of the jent_stuck test */
|
||||
if (ec->stuck) {
|
||||
/*
|
||||
* We only mix in the bit considered not appropriate
|
||||
* without the LSFR. The reason is that if we apply
|
||||
* the LSFR and we do not rotate, the 2nd bit with LSFR
|
||||
* will cancel out the first LSFR application on the
|
||||
* bad bit.
|
||||
*
|
||||
* And we do not rotate as we apply the next bit to the
|
||||
* current bit location again.
|
||||
*/
|
||||
ec->data ^= data;
|
||||
ec->stuck = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fibonacci LSFR with polynom of
|
||||
* x^64 + x^61 + x^56 + x^31 + x^28 + x^23 + 1 which is
|
||||
* primitive according to
|
||||
* http://poincare.matf.bg.ac.rs/~ezivkovm/publications/primpol1.pdf
|
||||
* (the shift values are the polynom values minus one
|
||||
* due to counting bits from 0 to 63). As the current
|
||||
* position is always the LSB, the polynom only needs
|
||||
* to shift data in from the left without wrap.
|
||||
*/
|
||||
ec->data ^= data;
|
||||
ec->data ^= ((ec->data >> 63) & 1);
|
||||
ec->data ^= ((ec->data >> 60) & 1);
|
||||
ec->data ^= ((ec->data >> 55) & 1);
|
||||
ec->data ^= ((ec->data >> 30) & 1);
|
||||
ec->data ^= ((ec->data >> 27) & 1);
|
||||
ec->data ^= ((ec->data >> 22) & 1);
|
||||
ec->data = rol64(ec->data, 1);
|
||||
|
||||
/*
|
||||
* We multiply the loop value with ->osr to obtain the
|
||||
* oversampling rate requested by the caller
|
||||
*/
|
||||
if (++k >= (DATA_SIZE_BITS * ec->osr))
|
||||
break;
|
||||
}
|
||||
if (ec->stir)
|
||||
jent_stir_pool(ec);
|
||||
}
|
||||
#pragma GCC pop_options
|
||||
|
||||
/**
|
||||
* The continuous test required by FIPS 140-2 -- the function automatically
|
||||
* primes the test if needed.
|
||||
*
|
||||
* Return:
|
||||
* 0 if FIPS test passed
|
||||
* < 0 if FIPS test failed
|
||||
*/
|
||||
static void jent_fips_test(struct rand_data *ec)
|
||||
{
|
||||
if (!fips_enabled)
|
||||
return;
|
||||
|
||||
/* prime the FIPS test */
|
||||
if (!ec->old_data) {
|
||||
ec->old_data = ec->data;
|
||||
jent_gen_entropy(ec);
|
||||
}
|
||||
|
||||
if (ec->data == ec->old_data)
|
||||
panic(DRIVER_NAME ": Duplicate output detected\n");
|
||||
|
||||
ec->old_data = ec->data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Entry function: Obtain entropy for the caller.
|
||||
*
|
||||
* This function invokes the entropy gathering logic as often to generate
|
||||
* as many bytes as requested by the caller. The entropy gathering logic
|
||||
* creates 64 bit per invocation.
|
||||
*
|
||||
* This function truncates the last 64 bit entropy value output to the exact
|
||||
* size specified by the caller.
|
||||
*
|
||||
* Input:
|
||||
* @ec Reference to entropy collector
|
||||
* @data pointer to buffer for storing random data -- buffer must already
|
||||
* exist
|
||||
* @len size of the buffer, specifying also the requested number of random
|
||||
* in bytes
|
||||
*
|
||||
* @return 0 when request is fulfilled or an error
|
||||
*
|
||||
* The following error codes can occur:
|
||||
* -1 entropy_collector is NULL
|
||||
*/
|
||||
static ssize_t jent_read_entropy(struct rand_data *ec, u8 *data, size_t len)
|
||||
{
|
||||
u8 *p = data;
|
||||
|
||||
if (!ec)
|
||||
return -EINVAL;
|
||||
|
||||
while (0 < len) {
|
||||
size_t tocopy;
|
||||
|
||||
jent_gen_entropy(ec);
|
||||
jent_fips_test(ec);
|
||||
if ((DATA_SIZE_BITS / 8) < len)
|
||||
tocopy = (DATA_SIZE_BITS / 8);
|
||||
else
|
||||
tocopy = len;
|
||||
memcpy(p, &ec->data, tocopy);
|
||||
|
||||
len -= tocopy;
|
||||
p += tocopy;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
* Initialization logic
|
||||
***************************************************************************/
|
||||
|
||||
static struct rand_data *jent_entropy_collector_alloc(unsigned int osr,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct rand_data *entropy_collector;
|
||||
|
||||
entropy_collector = kzalloc(sizeof(struct rand_data), GFP_KERNEL);
|
||||
if (!entropy_collector)
|
||||
return NULL;
|
||||
|
||||
if (!(flags & JENT_DISABLE_MEMORY_ACCESS)) {
|
||||
/* Allocate memory for adding variations based on memory
|
||||
* access
|
||||
*/
|
||||
entropy_collector->mem = kzalloc(JENT_MEMORY_SIZE, GFP_KERNEL);
|
||||
if (!entropy_collector->mem) {
|
||||
kfree(entropy_collector);
|
||||
return NULL;
|
||||
}
|
||||
entropy_collector->memblocksize = JENT_MEMORY_BLOCKSIZE;
|
||||
entropy_collector->memblocks = JENT_MEMORY_BLOCKS;
|
||||
entropy_collector->memaccessloops = JENT_MEMORY_ACCESSLOOPS;
|
||||
}
|
||||
|
||||
/* verify and set the oversampling rate */
|
||||
if (0 == osr)
|
||||
osr = 1; /* minimum sampling rate is 1 */
|
||||
entropy_collector->osr = osr;
|
||||
|
||||
entropy_collector->stir = 1;
|
||||
if (flags & JENT_DISABLE_STIR)
|
||||
entropy_collector->stir = 0;
|
||||
if (flags & JENT_DISABLE_UNBIAS)
|
||||
entropy_collector->disable_unbias = 1;
|
||||
|
||||
/* fill the data pad with non-zero values */
|
||||
jent_gen_entropy(entropy_collector);
|
||||
|
||||
return entropy_collector;
|
||||
}
|
||||
|
||||
static void jent_entropy_collector_free(struct rand_data *entropy_collector)
|
||||
{
|
||||
if (entropy_collector->mem)
|
||||
kzfree(entropy_collector->mem);
|
||||
entropy_collector->mem = NULL;
|
||||
if (entropy_collector)
|
||||
kzfree(entropy_collector);
|
||||
entropy_collector = NULL;
|
||||
}
|
||||
|
||||
static int jent_entropy_init(void)
|
||||
{
|
||||
int i;
|
||||
__u64 delta_sum = 0;
|
||||
__u64 old_delta = 0;
|
||||
int time_backwards = 0;
|
||||
int count_var = 0;
|
||||
int count_mod = 0;
|
||||
|
||||
/* We could perform statistical tests here, but the problem is
|
||||
* that we only have a few loop counts to do testing. These
|
||||
* loop counts may show some slight skew and we produce
|
||||
* false positives.
|
||||
*
|
||||
* Moreover, only old systems show potentially problematic
|
||||
* jitter entropy that could potentially be caught here. But
|
||||
* the RNG is intended for hardware that is available or widely
|
||||
* used, but not old systems that are long out of favor. Thus,
|
||||
* no statistical tests.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We could add a check for system capabilities such as clock_getres or
|
||||
* check for CONFIG_X86_TSC, but it does not make much sense as the
|
||||
* following sanity checks verify that we have a high-resolution
|
||||
* timer.
|
||||
*/
|
||||
/*
|
||||
* TESTLOOPCOUNT needs some loops to identify edge systems. 100 is
|
||||
* definitely too little.
|
||||
*/
|
||||
#define TESTLOOPCOUNT 300
|
||||
#define CLEARCACHE 100
|
||||
for (i = 0; (TESTLOOPCOUNT + CLEARCACHE) > i; i++) {
|
||||
__u64 time = 0;
|
||||
__u64 time2 = 0;
|
||||
__u64 folded = 0;
|
||||
__u64 delta = 0;
|
||||
unsigned int lowdelta = 0;
|
||||
|
||||
jent_get_nstime(&time);
|
||||
jent_fold_time(NULL, time, &folded, 1<<MIN_FOLD_LOOP_BIT);
|
||||
jent_get_nstime(&time2);
|
||||
|
||||
/* test whether timer works */
|
||||
if (!time || !time2)
|
||||
return JENT_ENOTIME;
|
||||
delta = time2 - time;
|
||||
/*
|
||||
* test whether timer is fine grained enough to provide
|
||||
* delta even when called shortly after each other -- this
|
||||
* implies that we also have a high resolution timer
|
||||
*/
|
||||
if (!delta)
|
||||
return JENT_ECOARSETIME;
|
||||
|
||||
/*
|
||||
* up to here we did not modify any variable that will be
|
||||
* evaluated later, but we already performed some work. Thus we
|
||||
* already have had an impact on the caches, branch prediction,
|
||||
* etc. with the goal to clear it to get the worst case
|
||||
* measurements.
|
||||
*/
|
||||
if (CLEARCACHE > i)
|
||||
continue;
|
||||
|
||||
/* test whether we have an increasing timer */
|
||||
if (!(time2 > time))
|
||||
time_backwards++;
|
||||
|
||||
/*
|
||||
* Avoid modulo of 64 bit integer to allow code to compile
|
||||
* on 32 bit architectures.
|
||||
*/
|
||||
lowdelta = time2 - time;
|
||||
if (!(lowdelta % 100))
|
||||
count_mod++;
|
||||
|
||||
/*
|
||||
* ensure that we have a varying delta timer which is necessary
|
||||
* for the calculation of entropy -- perform this check
|
||||
* only after the first loop is executed as we need to prime
|
||||
* the old_data value
|
||||
*/
|
||||
if (i) {
|
||||
if (delta != old_delta)
|
||||
count_var++;
|
||||
if (delta > old_delta)
|
||||
delta_sum += (delta - old_delta);
|
||||
else
|
||||
delta_sum += (old_delta - delta);
|
||||
}
|
||||
old_delta = delta;
|
||||
}
|
||||
|
||||
/*
|
||||
* we allow up to three times the time running backwards.
|
||||
* CLOCK_REALTIME is affected by adjtime and NTP operations. Thus,
|
||||
* if such an operation just happens to interfere with our test, it
|
||||
* should not fail. The value of 3 should cover the NTP case being
|
||||
* performed during our test run.
|
||||
*/
|
||||
if (3 < time_backwards)
|
||||
return JENT_ENOMONOTONIC;
|
||||
/* Error if the time variances are always identical */
|
||||
if (!delta_sum)
|
||||
return JENT_EVARVAR;
|
||||
|
||||
/*
|
||||
* Variations of deltas of time must on average be larger
|
||||
* than 1 to ensure the entropy estimation
|
||||
* implied with 1 is preserved
|
||||
*/
|
||||
if (delta_sum <= 1)
|
||||
return JENT_EMINVARVAR;
|
||||
|
||||
/*
|
||||
* Ensure that we have variations in the time stamp below 10 for at
|
||||
* least 10% of all checks -- on some platforms, the counter
|
||||
* increments in multiples of 100, but not always
|
||||
*/
|
||||
if ((TESTLOOPCOUNT/10 * 9) < count_mod)
|
||||
return JENT_ECOARSETIME;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
* Kernel crypto API interface
|
||||
***************************************************************************/
|
||||
|
||||
struct jitterentropy {
|
||||
spinlock_t jent_lock;
|
||||
struct rand_data *entropy_collector;
|
||||
};
|
||||
|
||||
static int jent_kcapi_init(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct jitterentropy *rng = crypto_tfm_ctx(tfm);
|
||||
int ret = 0;
|
||||
|
||||
rng->entropy_collector = jent_entropy_collector_alloc(1, 0);
|
||||
if (!rng->entropy_collector)
|
||||
ret = -ENOMEM;
|
||||
|
||||
spin_lock_init(&rng->jent_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void jent_kcapi_cleanup(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct jitterentropy *rng = crypto_tfm_ctx(tfm);
|
||||
|
||||
spin_lock(&rng->jent_lock);
|
||||
if (rng->entropy_collector)
|
||||
jent_entropy_collector_free(rng->entropy_collector);
|
||||
rng->entropy_collector = NULL;
|
||||
spin_unlock(&rng->jent_lock);
|
||||
}
|
||||
|
||||
static int jent_kcapi_random(struct crypto_rng *tfm,
|
||||
const u8 *src, unsigned int slen,
|
||||
u8 *rdata, unsigned int dlen)
|
||||
{
|
||||
struct jitterentropy *rng = crypto_rng_ctx(tfm);
|
||||
int ret = 0;
|
||||
|
||||
spin_lock(&rng->jent_lock);
|
||||
ret = jent_read_entropy(rng->entropy_collector, rdata, dlen);
|
||||
spin_unlock(&rng->jent_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int jent_kcapi_reset(struct crypto_rng *tfm,
|
||||
const u8 *seed, unsigned int slen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rng_alg jent_alg = {
|
||||
.generate = jent_kcapi_random,
|
||||
.seed = jent_kcapi_reset,
|
||||
.seedsize = 0,
|
||||
.base = {
|
||||
.cra_name = "jitterentropy_rng",
|
||||
.cra_driver_name = "jitterentropy_rng",
|
||||
.cra_priority = 100,
|
||||
.cra_ctxsize = sizeof(struct jitterentropy),
|
||||
.cra_module = THIS_MODULE,
|
||||
.cra_init = jent_kcapi_init,
|
||||
.cra_exit = jent_kcapi_cleanup,
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
static int __init jent_mod_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = jent_entropy_init();
|
||||
if (ret) {
|
||||
pr_info(DRIVER_NAME ": Initialization failed with host not compliant with requirements: %d\n", ret);
|
||||
return -EFAULT;
|
||||
}
|
||||
return crypto_register_rng(&jent_alg);
|
||||
}
|
||||
|
||||
static void __exit jent_mod_exit(void)
|
||||
{
|
||||
crypto_unregister_rng(&jent_alg);
|
||||
}
|
||||
|
||||
module_init(jent_mod_init);
|
||||
module_exit(jent_mod_exit);
|
||||
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>");
|
||||
MODULE_DESCRIPTION("Non-physical True Random Number Generator based on CPU Jitter");
|
||||
MODULE_ALIAS_CRYPTO("jitterentropy_rng");
|
@ -1,66 +0,0 @@
|
||||
/*
|
||||
* RNG implementation using standard kernel RNG.
|
||||
*
|
||||
* Copyright (c) 2008 Herbert Xu <herbert@gondor.apana.org.au>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <crypto/internal/rng.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/random.h>
|
||||
|
||||
static int krng_get_random(struct crypto_rng *tfm, u8 *rdata, unsigned int dlen)
|
||||
{
|
||||
get_random_bytes(rdata, dlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int krng_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct crypto_alg krng_alg = {
|
||||
.cra_name = "stdrng",
|
||||
.cra_driver_name = "krng",
|
||||
.cra_priority = 200,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_RNG,
|
||||
.cra_ctxsize = 0,
|
||||
.cra_type = &crypto_rng_type,
|
||||
.cra_module = THIS_MODULE,
|
||||
.cra_u = {
|
||||
.rng = {
|
||||
.rng_make_random = krng_get_random,
|
||||
.rng_reset = krng_reset,
|
||||
.seedsize = 0,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* Module initalization */
|
||||
static int __init krng_mod_init(void)
|
||||
{
|
||||
return crypto_register_alg(&krng_alg);
|
||||
}
|
||||
|
||||
static void __exit krng_mod_fini(void)
|
||||
{
|
||||
crypto_unregister_alg(&krng_alg);
|
||||
return;
|
||||
}
|
||||
|
||||
module_init(krng_mod_init);
|
||||
module_exit(krng_mod_fini);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Kernel Random Number Generator");
|
||||
MODULE_ALIAS_CRYPTO("stdrng");
|
||||
MODULE_ALIAS_CRYPTO("krng");
|
@ -51,10 +51,10 @@ static int md5_init(struct shash_desc *desc)
|
||||
{
|
||||
struct md5_state *mctx = shash_desc_ctx(desc);
|
||||
|
||||
mctx->hash[0] = 0x67452301;
|
||||
mctx->hash[1] = 0xefcdab89;
|
||||
mctx->hash[2] = 0x98badcfe;
|
||||
mctx->hash[3] = 0x10325476;
|
||||
mctx->hash[0] = MD5_H0;
|
||||
mctx->hash[1] = MD5_H1;
|
||||
mctx->hash[2] = MD5_H2;
|
||||
mctx->hash[3] = MD5_H3;
|
||||
mctx->byte_count = 0;
|
||||
|
||||
return 0;
|
||||
|
@ -38,11 +38,6 @@ static int crypto_pcomp_init(struct crypto_tfm *tfm, u32 type, u32 mask)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int crypto_pcomp_extsize(struct crypto_alg *alg)
|
||||
{
|
||||
return alg->cra_ctxsize;
|
||||
}
|
||||
|
||||
static int crypto_pcomp_init_tfm(struct crypto_tfm *tfm)
|
||||
{
|
||||
return 0;
|
||||
@ -77,7 +72,7 @@ static void crypto_pcomp_show(struct seq_file *m, struct crypto_alg *alg)
|
||||
}
|
||||
|
||||
static const struct crypto_type crypto_pcomp_type = {
|
||||
.extsize = crypto_pcomp_extsize,
|
||||
.extsize = crypto_alg_extsize,
|
||||
.init = crypto_pcomp_init,
|
||||
.init_tfm = crypto_pcomp_init_tfm,
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
213
crypto/pcrypt.c
213
crypto/pcrypt.c
@ -20,6 +20,7 @@
|
||||
|
||||
#include <crypto/algapi.h>
|
||||
#include <crypto/internal/aead.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
@ -60,8 +61,8 @@ static struct padata_pcrypt pdecrypt;
|
||||
static struct kset *pcrypt_kset;
|
||||
|
||||
struct pcrypt_instance_ctx {
|
||||
struct crypto_spawn spawn;
|
||||
unsigned int tfm_count;
|
||||
struct crypto_aead_spawn spawn;
|
||||
atomic_t tfm_count;
|
||||
};
|
||||
|
||||
struct pcrypt_aead_ctx {
|
||||
@ -122,14 +123,6 @@ static void pcrypt_aead_serial(struct padata_priv *padata)
|
||||
aead_request_complete(req->base.data, padata->info);
|
||||
}
|
||||
|
||||
static void pcrypt_aead_giv_serial(struct padata_priv *padata)
|
||||
{
|
||||
struct pcrypt_request *preq = pcrypt_padata_request(padata);
|
||||
struct aead_givcrypt_request *req = pcrypt_request_ctx(preq);
|
||||
|
||||
aead_request_complete(req->areq.base.data, padata->info);
|
||||
}
|
||||
|
||||
static void pcrypt_aead_done(struct crypto_async_request *areq, int err)
|
||||
{
|
||||
struct aead_request *req = areq->data;
|
||||
@ -175,7 +168,7 @@ static int pcrypt_aead_encrypt(struct aead_request *req)
|
||||
pcrypt_aead_done, req);
|
||||
aead_request_set_crypt(creq, req->src, req->dst,
|
||||
req->cryptlen, req->iv);
|
||||
aead_request_set_assoc(creq, req->assoc, req->assoclen);
|
||||
aead_request_set_ad(creq, req->assoclen);
|
||||
|
||||
err = pcrypt_do_parallel(padata, &ctx->cb_cpu, &pencrypt);
|
||||
if (!err)
|
||||
@ -217,7 +210,7 @@ static int pcrypt_aead_decrypt(struct aead_request *req)
|
||||
pcrypt_aead_done, req);
|
||||
aead_request_set_crypt(creq, req->src, req->dst,
|
||||
req->cryptlen, req->iv);
|
||||
aead_request_set_assoc(creq, req->assoc, req->assoclen);
|
||||
aead_request_set_ad(creq, req->assoclen);
|
||||
|
||||
err = pcrypt_do_parallel(padata, &ctx->cb_cpu, &pdecrypt);
|
||||
if (!err)
|
||||
@ -226,182 +219,134 @@ static int pcrypt_aead_decrypt(struct aead_request *req)
|
||||
return err;
|
||||
}
|
||||
|
||||
static void pcrypt_aead_givenc(struct padata_priv *padata)
|
||||
{
|
||||
struct pcrypt_request *preq = pcrypt_padata_request(padata);
|
||||
struct aead_givcrypt_request *req = pcrypt_request_ctx(preq);
|
||||
|
||||
padata->info = crypto_aead_givencrypt(req);
|
||||
|
||||
if (padata->info == -EINPROGRESS)
|
||||
return;
|
||||
|
||||
padata_do_serial(padata);
|
||||
}
|
||||
|
||||
static int pcrypt_aead_givencrypt(struct aead_givcrypt_request *req)
|
||||
{
|
||||
int err;
|
||||
struct aead_request *areq = &req->areq;
|
||||
struct pcrypt_request *preq = aead_request_ctx(areq);
|
||||
struct aead_givcrypt_request *creq = pcrypt_request_ctx(preq);
|
||||
struct padata_priv *padata = pcrypt_request_padata(preq);
|
||||
struct crypto_aead *aead = aead_givcrypt_reqtfm(req);
|
||||
struct pcrypt_aead_ctx *ctx = crypto_aead_ctx(aead);
|
||||
u32 flags = aead_request_flags(areq);
|
||||
|
||||
memset(padata, 0, sizeof(struct padata_priv));
|
||||
|
||||
padata->parallel = pcrypt_aead_givenc;
|
||||
padata->serial = pcrypt_aead_giv_serial;
|
||||
|
||||
aead_givcrypt_set_tfm(creq, ctx->child);
|
||||
aead_givcrypt_set_callback(creq, flags & ~CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
pcrypt_aead_done, areq);
|
||||
aead_givcrypt_set_crypt(creq, areq->src, areq->dst,
|
||||
areq->cryptlen, areq->iv);
|
||||
aead_givcrypt_set_assoc(creq, areq->assoc, areq->assoclen);
|
||||
aead_givcrypt_set_giv(creq, req->giv, req->seq);
|
||||
|
||||
err = pcrypt_do_parallel(padata, &ctx->cb_cpu, &pencrypt);
|
||||
if (!err)
|
||||
return -EINPROGRESS;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pcrypt_aead_init_tfm(struct crypto_tfm *tfm)
|
||||
static int pcrypt_aead_init_tfm(struct crypto_aead *tfm)
|
||||
{
|
||||
int cpu, cpu_index;
|
||||
struct crypto_instance *inst = crypto_tfm_alg_instance(tfm);
|
||||
struct pcrypt_instance_ctx *ictx = crypto_instance_ctx(inst);
|
||||
struct pcrypt_aead_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
struct aead_instance *inst = aead_alg_instance(tfm);
|
||||
struct pcrypt_instance_ctx *ictx = aead_instance_ctx(inst);
|
||||
struct pcrypt_aead_ctx *ctx = crypto_aead_ctx(tfm);
|
||||
struct crypto_aead *cipher;
|
||||
|
||||
ictx->tfm_count++;
|
||||
|
||||
cpu_index = ictx->tfm_count % cpumask_weight(cpu_online_mask);
|
||||
cpu_index = (unsigned int)atomic_inc_return(&ictx->tfm_count) %
|
||||
cpumask_weight(cpu_online_mask);
|
||||
|
||||
ctx->cb_cpu = cpumask_first(cpu_online_mask);
|
||||
for (cpu = 0; cpu < cpu_index; cpu++)
|
||||
ctx->cb_cpu = cpumask_next(ctx->cb_cpu, cpu_online_mask);
|
||||
|
||||
cipher = crypto_spawn_aead(crypto_instance_ctx(inst));
|
||||
cipher = crypto_spawn_aead(&ictx->spawn);
|
||||
|
||||
if (IS_ERR(cipher))
|
||||
return PTR_ERR(cipher);
|
||||
|
||||
ctx->child = cipher;
|
||||
tfm->crt_aead.reqsize = sizeof(struct pcrypt_request)
|
||||
+ sizeof(struct aead_givcrypt_request)
|
||||
+ crypto_aead_reqsize(cipher);
|
||||
crypto_aead_set_reqsize(tfm, sizeof(struct pcrypt_request) +
|
||||
sizeof(struct aead_request) +
|
||||
crypto_aead_reqsize(cipher));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pcrypt_aead_exit_tfm(struct crypto_tfm *tfm)
|
||||
static void pcrypt_aead_exit_tfm(struct crypto_aead *tfm)
|
||||
{
|
||||
struct pcrypt_aead_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
struct pcrypt_aead_ctx *ctx = crypto_aead_ctx(tfm);
|
||||
|
||||
crypto_free_aead(ctx->child);
|
||||
}
|
||||
|
||||
static struct crypto_instance *pcrypt_alloc_instance(struct crypto_alg *alg)
|
||||
static int pcrypt_init_instance(struct crypto_instance *inst,
|
||||
struct crypto_alg *alg)
|
||||
{
|
||||
struct crypto_instance *inst;
|
||||
struct pcrypt_instance_ctx *ctx;
|
||||
int err;
|
||||
|
||||
inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL);
|
||||
if (!inst) {
|
||||
inst = ERR_PTR(-ENOMEM);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = -ENAMETOOLONG;
|
||||
if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME,
|
||||
"pcrypt(%s)", alg->cra_driver_name) >= CRYPTO_MAX_ALG_NAME)
|
||||
goto out_free_inst;
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
memcpy(inst->alg.cra_name, alg->cra_name, CRYPTO_MAX_ALG_NAME);
|
||||
|
||||
ctx = crypto_instance_ctx(inst);
|
||||
err = crypto_init_spawn(&ctx->spawn, alg, inst,
|
||||
CRYPTO_ALG_TYPE_MASK);
|
||||
if (err)
|
||||
goto out_free_inst;
|
||||
|
||||
inst->alg.cra_priority = alg->cra_priority + 100;
|
||||
inst->alg.cra_blocksize = alg->cra_blocksize;
|
||||
inst->alg.cra_alignmask = alg->cra_alignmask;
|
||||
|
||||
out:
|
||||
return inst;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcrypt_create_aead(struct crypto_template *tmpl, struct rtattr **tb,
|
||||
u32 type, u32 mask)
|
||||
{
|
||||
struct pcrypt_instance_ctx *ctx;
|
||||
struct aead_instance *inst;
|
||||
struct aead_alg *alg;
|
||||
const char *name;
|
||||
int err;
|
||||
|
||||
name = crypto_attr_alg_name(tb[1]);
|
||||
if (IS_ERR(name))
|
||||
return PTR_ERR(name);
|
||||
|
||||
inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL);
|
||||
if (!inst)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx = aead_instance_ctx(inst);
|
||||
crypto_set_aead_spawn(&ctx->spawn, aead_crypto_instance(inst));
|
||||
|
||||
err = crypto_grab_aead(&ctx->spawn, name, 0, 0);
|
||||
if (err)
|
||||
goto out_free_inst;
|
||||
|
||||
alg = crypto_spawn_aead_alg(&ctx->spawn);
|
||||
err = pcrypt_init_instance(aead_crypto_instance(inst), &alg->base);
|
||||
if (err)
|
||||
goto out_drop_aead;
|
||||
|
||||
inst->alg.ivsize = crypto_aead_alg_ivsize(alg);
|
||||
inst->alg.maxauthsize = crypto_aead_alg_maxauthsize(alg);
|
||||
|
||||
inst->alg.base.cra_ctxsize = sizeof(struct pcrypt_aead_ctx);
|
||||
|
||||
inst->alg.init = pcrypt_aead_init_tfm;
|
||||
inst->alg.exit = pcrypt_aead_exit_tfm;
|
||||
|
||||
inst->alg.setkey = pcrypt_aead_setkey;
|
||||
inst->alg.setauthsize = pcrypt_aead_setauthsize;
|
||||
inst->alg.encrypt = pcrypt_aead_encrypt;
|
||||
inst->alg.decrypt = pcrypt_aead_decrypt;
|
||||
|
||||
err = aead_register_instance(tmpl, inst);
|
||||
if (err)
|
||||
goto out_drop_aead;
|
||||
|
||||
out:
|
||||
return err;
|
||||
|
||||
out_drop_aead:
|
||||
crypto_drop_aead(&ctx->spawn);
|
||||
out_free_inst:
|
||||
kfree(inst);
|
||||
inst = ERR_PTR(err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static struct crypto_instance *pcrypt_alloc_aead(struct rtattr **tb,
|
||||
u32 type, u32 mask)
|
||||
{
|
||||
struct crypto_instance *inst;
|
||||
struct crypto_alg *alg;
|
||||
|
||||
alg = crypto_get_attr_alg(tb, type, (mask & CRYPTO_ALG_TYPE_MASK));
|
||||
if (IS_ERR(alg))
|
||||
return ERR_CAST(alg);
|
||||
|
||||
inst = pcrypt_alloc_instance(alg);
|
||||
if (IS_ERR(inst))
|
||||
goto out_put_alg;
|
||||
|
||||
inst->alg.cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC;
|
||||
inst->alg.cra_type = &crypto_aead_type;
|
||||
|
||||
inst->alg.cra_aead.ivsize = alg->cra_aead.ivsize;
|
||||
inst->alg.cra_aead.geniv = alg->cra_aead.geniv;
|
||||
inst->alg.cra_aead.maxauthsize = alg->cra_aead.maxauthsize;
|
||||
|
||||
inst->alg.cra_ctxsize = sizeof(struct pcrypt_aead_ctx);
|
||||
|
||||
inst->alg.cra_init = pcrypt_aead_init_tfm;
|
||||
inst->alg.cra_exit = pcrypt_aead_exit_tfm;
|
||||
|
||||
inst->alg.cra_aead.setkey = pcrypt_aead_setkey;
|
||||
inst->alg.cra_aead.setauthsize = pcrypt_aead_setauthsize;
|
||||
inst->alg.cra_aead.encrypt = pcrypt_aead_encrypt;
|
||||
inst->alg.cra_aead.decrypt = pcrypt_aead_decrypt;
|
||||
inst->alg.cra_aead.givencrypt = pcrypt_aead_givencrypt;
|
||||
|
||||
out_put_alg:
|
||||
crypto_mod_put(alg);
|
||||
return inst;
|
||||
}
|
||||
|
||||
static struct crypto_instance *pcrypt_alloc(struct rtattr **tb)
|
||||
static int pcrypt_create(struct crypto_template *tmpl, struct rtattr **tb)
|
||||
{
|
||||
struct crypto_attr_type *algt;
|
||||
|
||||
algt = crypto_get_attr_type(tb);
|
||||
if (IS_ERR(algt))
|
||||
return ERR_CAST(algt);
|
||||
return PTR_ERR(algt);
|
||||
|
||||
switch (algt->type & algt->mask & CRYPTO_ALG_TYPE_MASK) {
|
||||
case CRYPTO_ALG_TYPE_AEAD:
|
||||
return pcrypt_alloc_aead(tb, algt->type, algt->mask);
|
||||
return pcrypt_create_aead(tmpl, tb, algt->type, algt->mask);
|
||||
}
|
||||
|
||||
return ERR_PTR(-EINVAL);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void pcrypt_free(struct crypto_instance *inst)
|
||||
{
|
||||
struct pcrypt_instance_ctx *ctx = crypto_instance_ctx(inst);
|
||||
|
||||
crypto_drop_spawn(&ctx->spawn);
|
||||
crypto_drop_aead(&ctx->spawn);
|
||||
kfree(inst);
|
||||
}
|
||||
|
||||
@ -516,7 +461,7 @@ static void pcrypt_fini_padata(struct padata_pcrypt *pcrypt)
|
||||
|
||||
static struct crypto_template pcrypt_tmpl = {
|
||||
.name = "pcrypt",
|
||||
.alloc = pcrypt_alloc,
|
||||
.create = pcrypt_create,
|
||||
.free = pcrypt_free,
|
||||
.module = THIS_MODULE,
|
||||
};
|
||||
|
321
crypto/poly1305_generic.c
Normal file
321
crypto/poly1305_generic.c
Normal file
@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Poly1305 authenticator algorithm, RFC7539
|
||||
*
|
||||
* Copyright (C) 2015 Martin Willi
|
||||
*
|
||||
* Based on public domain code by Andrew Moon and Daniel J. Bernstein.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <crypto/algapi.h>
|
||||
#include <crypto/internal/hash.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define POLY1305_BLOCK_SIZE 16
|
||||
#define POLY1305_KEY_SIZE 32
|
||||
#define POLY1305_DIGEST_SIZE 16
|
||||
|
||||
struct poly1305_desc_ctx {
|
||||
/* key */
|
||||
u32 r[5];
|
||||
/* finalize key */
|
||||
u32 s[4];
|
||||
/* accumulator */
|
||||
u32 h[5];
|
||||
/* partial buffer */
|
||||
u8 buf[POLY1305_BLOCK_SIZE];
|
||||
/* bytes used in partial buffer */
|
||||
unsigned int buflen;
|
||||
/* r key has been set */
|
||||
bool rset;
|
||||
/* s key has been set */
|
||||
bool sset;
|
||||
};
|
||||
|
||||
static inline u64 mlt(u64 a, u64 b)
|
||||
{
|
||||
return a * b;
|
||||
}
|
||||
|
||||
static inline u32 sr(u64 v, u_char n)
|
||||
{
|
||||
return v >> n;
|
||||
}
|
||||
|
||||
static inline u32 and(u32 v, u32 mask)
|
||||
{
|
||||
return v & mask;
|
||||
}
|
||||
|
||||
static inline u32 le32_to_cpuvp(const void *p)
|
||||
{
|
||||
return le32_to_cpup(p);
|
||||
}
|
||||
|
||||
static int poly1305_init(struct shash_desc *desc)
|
||||
{
|
||||
struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
|
||||
|
||||
memset(dctx->h, 0, sizeof(dctx->h));
|
||||
dctx->buflen = 0;
|
||||
dctx->rset = false;
|
||||
dctx->sset = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int poly1305_setkey(struct crypto_shash *tfm,
|
||||
const u8 *key, unsigned int keylen)
|
||||
{
|
||||
/* Poly1305 requires a unique key for each tag, which implies that
|
||||
* we can't set it on the tfm that gets accessed by multiple users
|
||||
* simultaneously. Instead we expect the key as the first 32 bytes in
|
||||
* the update() call. */
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static void poly1305_setrkey(struct poly1305_desc_ctx *dctx, const u8 *key)
|
||||
{
|
||||
/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
|
||||
dctx->r[0] = (le32_to_cpuvp(key + 0) >> 0) & 0x3ffffff;
|
||||
dctx->r[1] = (le32_to_cpuvp(key + 3) >> 2) & 0x3ffff03;
|
||||
dctx->r[2] = (le32_to_cpuvp(key + 6) >> 4) & 0x3ffc0ff;
|
||||
dctx->r[3] = (le32_to_cpuvp(key + 9) >> 6) & 0x3f03fff;
|
||||
dctx->r[4] = (le32_to_cpuvp(key + 12) >> 8) & 0x00fffff;
|
||||
}
|
||||
|
||||
static void poly1305_setskey(struct poly1305_desc_ctx *dctx, const u8 *key)
|
||||
{
|
||||
dctx->s[0] = le32_to_cpuvp(key + 0);
|
||||
dctx->s[1] = le32_to_cpuvp(key + 4);
|
||||
dctx->s[2] = le32_to_cpuvp(key + 8);
|
||||
dctx->s[3] = le32_to_cpuvp(key + 12);
|
||||
}
|
||||
|
||||
static unsigned int poly1305_blocks(struct poly1305_desc_ctx *dctx,
|
||||
const u8 *src, unsigned int srclen,
|
||||
u32 hibit)
|
||||
{
|
||||
u32 r0, r1, r2, r3, r4;
|
||||
u32 s1, s2, s3, s4;
|
||||
u32 h0, h1, h2, h3, h4;
|
||||
u64 d0, d1, d2, d3, d4;
|
||||
|
||||
if (unlikely(!dctx->sset)) {
|
||||
if (!dctx->rset && srclen >= POLY1305_BLOCK_SIZE) {
|
||||
poly1305_setrkey(dctx, src);
|
||||
src += POLY1305_BLOCK_SIZE;
|
||||
srclen -= POLY1305_BLOCK_SIZE;
|
||||
dctx->rset = true;
|
||||
}
|
||||
if (srclen >= POLY1305_BLOCK_SIZE) {
|
||||
poly1305_setskey(dctx, src);
|
||||
src += POLY1305_BLOCK_SIZE;
|
||||
srclen -= POLY1305_BLOCK_SIZE;
|
||||
dctx->sset = true;
|
||||
}
|
||||
}
|
||||
|
||||
r0 = dctx->r[0];
|
||||
r1 = dctx->r[1];
|
||||
r2 = dctx->r[2];
|
||||
r3 = dctx->r[3];
|
||||
r4 = dctx->r[4];
|
||||
|
||||
s1 = r1 * 5;
|
||||
s2 = r2 * 5;
|
||||
s3 = r3 * 5;
|
||||
s4 = r4 * 5;
|
||||
|
||||
h0 = dctx->h[0];
|
||||
h1 = dctx->h[1];
|
||||
h2 = dctx->h[2];
|
||||
h3 = dctx->h[3];
|
||||
h4 = dctx->h[4];
|
||||
|
||||
while (likely(srclen >= POLY1305_BLOCK_SIZE)) {
|
||||
|
||||
/* h += m[i] */
|
||||
h0 += (le32_to_cpuvp(src + 0) >> 0) & 0x3ffffff;
|
||||
h1 += (le32_to_cpuvp(src + 3) >> 2) & 0x3ffffff;
|
||||
h2 += (le32_to_cpuvp(src + 6) >> 4) & 0x3ffffff;
|
||||
h3 += (le32_to_cpuvp(src + 9) >> 6) & 0x3ffffff;
|
||||
h4 += (le32_to_cpuvp(src + 12) >> 8) | hibit;
|
||||
|
||||
/* h *= r */
|
||||
d0 = mlt(h0, r0) + mlt(h1, s4) + mlt(h2, s3) +
|
||||
mlt(h3, s2) + mlt(h4, s1);
|
||||
d1 = mlt(h0, r1) + mlt(h1, r0) + mlt(h2, s4) +
|
||||
mlt(h3, s3) + mlt(h4, s2);
|
||||
d2 = mlt(h0, r2) + mlt(h1, r1) + mlt(h2, r0) +
|
||||
mlt(h3, s4) + mlt(h4, s3);
|
||||
d3 = mlt(h0, r3) + mlt(h1, r2) + mlt(h2, r1) +
|
||||
mlt(h3, r0) + mlt(h4, s4);
|
||||
d4 = mlt(h0, r4) + mlt(h1, r3) + mlt(h2, r2) +
|
||||
mlt(h3, r1) + mlt(h4, r0);
|
||||
|
||||
/* (partial) h %= p */
|
||||
d1 += sr(d0, 26); h0 = and(d0, 0x3ffffff);
|
||||
d2 += sr(d1, 26); h1 = and(d1, 0x3ffffff);
|
||||
d3 += sr(d2, 26); h2 = and(d2, 0x3ffffff);
|
||||
d4 += sr(d3, 26); h3 = and(d3, 0x3ffffff);
|
||||
h0 += sr(d4, 26) * 5; h4 = and(d4, 0x3ffffff);
|
||||
h1 += h0 >> 26; h0 = h0 & 0x3ffffff;
|
||||
|
||||
src += POLY1305_BLOCK_SIZE;
|
||||
srclen -= POLY1305_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
dctx->h[0] = h0;
|
||||
dctx->h[1] = h1;
|
||||
dctx->h[2] = h2;
|
||||
dctx->h[3] = h3;
|
||||
dctx->h[4] = h4;
|
||||
|
||||
return srclen;
|
||||
}
|
||||
|
||||
static int poly1305_update(struct shash_desc *desc,
|
||||
const u8 *src, unsigned int srclen)
|
||||
{
|
||||
struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
|
||||
unsigned int bytes;
|
||||
|
||||
if (unlikely(dctx->buflen)) {
|
||||
bytes = min(srclen, POLY1305_BLOCK_SIZE - dctx->buflen);
|
||||
memcpy(dctx->buf + dctx->buflen, src, bytes);
|
||||
src += bytes;
|
||||
srclen -= bytes;
|
||||
dctx->buflen += bytes;
|
||||
|
||||
if (dctx->buflen == POLY1305_BLOCK_SIZE) {
|
||||
poly1305_blocks(dctx, dctx->buf,
|
||||
POLY1305_BLOCK_SIZE, 1 << 24);
|
||||
dctx->buflen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (likely(srclen >= POLY1305_BLOCK_SIZE)) {
|
||||
bytes = poly1305_blocks(dctx, src, srclen, 1 << 24);
|
||||
src += srclen - bytes;
|
||||
srclen = bytes;
|
||||
}
|
||||
|
||||
if (unlikely(srclen)) {
|
||||
dctx->buflen = srclen;
|
||||
memcpy(dctx->buf, src, srclen);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int poly1305_final(struct shash_desc *desc, u8 *dst)
|
||||
{
|
||||
struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
|
||||
__le32 *mac = (__le32 *)dst;
|
||||
u32 h0, h1, h2, h3, h4;
|
||||
u32 g0, g1, g2, g3, g4;
|
||||
u32 mask;
|
||||
u64 f = 0;
|
||||
|
||||
if (unlikely(!dctx->sset))
|
||||
return -ENOKEY;
|
||||
|
||||
if (unlikely(dctx->buflen)) {
|
||||
dctx->buf[dctx->buflen++] = 1;
|
||||
memset(dctx->buf + dctx->buflen, 0,
|
||||
POLY1305_BLOCK_SIZE - dctx->buflen);
|
||||
poly1305_blocks(dctx, dctx->buf, POLY1305_BLOCK_SIZE, 0);
|
||||
}
|
||||
|
||||
/* fully carry h */
|
||||
h0 = dctx->h[0];
|
||||
h1 = dctx->h[1];
|
||||
h2 = dctx->h[2];
|
||||
h3 = dctx->h[3];
|
||||
h4 = dctx->h[4];
|
||||
|
||||
h2 += (h1 >> 26); h1 = h1 & 0x3ffffff;
|
||||
h3 += (h2 >> 26); h2 = h2 & 0x3ffffff;
|
||||
h4 += (h3 >> 26); h3 = h3 & 0x3ffffff;
|
||||
h0 += (h4 >> 26) * 5; h4 = h4 & 0x3ffffff;
|
||||
h1 += (h0 >> 26); h0 = h0 & 0x3ffffff;
|
||||
|
||||
/* compute h + -p */
|
||||
g0 = h0 + 5;
|
||||
g1 = h1 + (g0 >> 26); g0 &= 0x3ffffff;
|
||||
g2 = h2 + (g1 >> 26); g1 &= 0x3ffffff;
|
||||
g3 = h3 + (g2 >> 26); g2 &= 0x3ffffff;
|
||||
g4 = h4 + (g3 >> 26) - (1 << 26); g3 &= 0x3ffffff;
|
||||
|
||||
/* select h if h < p, or h + -p if h >= p */
|
||||
mask = (g4 >> ((sizeof(u32) * 8) - 1)) - 1;
|
||||
g0 &= mask;
|
||||
g1 &= mask;
|
||||
g2 &= mask;
|
||||
g3 &= mask;
|
||||
g4 &= mask;
|
||||
mask = ~mask;
|
||||
h0 = (h0 & mask) | g0;
|
||||
h1 = (h1 & mask) | g1;
|
||||
h2 = (h2 & mask) | g2;
|
||||
h3 = (h3 & mask) | g3;
|
||||
h4 = (h4 & mask) | g4;
|
||||
|
||||
/* h = h % (2^128) */
|
||||
h0 = (h0 >> 0) | (h1 << 26);
|
||||
h1 = (h1 >> 6) | (h2 << 20);
|
||||
h2 = (h2 >> 12) | (h3 << 14);
|
||||
h3 = (h3 >> 18) | (h4 << 8);
|
||||
|
||||
/* mac = (h + s) % (2^128) */
|
||||
f = (f >> 32) + h0 + dctx->s[0]; mac[0] = cpu_to_le32(f);
|
||||
f = (f >> 32) + h1 + dctx->s[1]; mac[1] = cpu_to_le32(f);
|
||||
f = (f >> 32) + h2 + dctx->s[2]; mac[2] = cpu_to_le32(f);
|
||||
f = (f >> 32) + h3 + dctx->s[3]; mac[3] = cpu_to_le32(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct shash_alg poly1305_alg = {
|
||||
.digestsize = POLY1305_DIGEST_SIZE,
|
||||
.init = poly1305_init,
|
||||
.update = poly1305_update,
|
||||
.final = poly1305_final,
|
||||
.setkey = poly1305_setkey,
|
||||
.descsize = sizeof(struct poly1305_desc_ctx),
|
||||
.base = {
|
||||
.cra_name = "poly1305",
|
||||
.cra_driver_name = "poly1305-generic",
|
||||
.cra_priority = 100,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_SHASH,
|
||||
.cra_alignmask = sizeof(u32) - 1,
|
||||
.cra_blocksize = POLY1305_BLOCK_SIZE,
|
||||
.cra_module = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init poly1305_mod_init(void)
|
||||
{
|
||||
return crypto_register_shash(&poly1305_alg);
|
||||
}
|
||||
|
||||
static void __exit poly1305_mod_exit(void)
|
||||
{
|
||||
crypto_unregister_shash(&poly1305_alg);
|
||||
}
|
||||
|
||||
module_init(poly1305_mod_init);
|
||||
module_exit(poly1305_mod_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
|
||||
MODULE_DESCRIPTION("Poly1305 authenticator");
|
||||
MODULE_ALIAS_CRYPTO("poly1305");
|
||||
MODULE_ALIAS_CRYPTO("poly1305-generic");
|
@ -20,47 +20,8 @@
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include "internal.h"
|
||||
|
||||
#ifdef CONFIG_CRYPTO_FIPS
|
||||
static struct ctl_table crypto_sysctl_table[] = {
|
||||
{
|
||||
.procname = "fips_enabled",
|
||||
.data = &fips_enabled,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0444,
|
||||
.proc_handler = proc_dointvec
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct ctl_table crypto_dir_table[] = {
|
||||
{
|
||||
.procname = "crypto",
|
||||
.mode = 0555,
|
||||
.child = crypto_sysctl_table
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct ctl_table_header *crypto_sysctls;
|
||||
|
||||
static void crypto_proc_fips_init(void)
|
||||
{
|
||||
crypto_sysctls = register_sysctl_table(crypto_dir_table);
|
||||
}
|
||||
|
||||
static void crypto_proc_fips_exit(void)
|
||||
{
|
||||
if (crypto_sysctls)
|
||||
unregister_sysctl_table(crypto_sysctls);
|
||||
}
|
||||
#else
|
||||
#define crypto_proc_fips_init()
|
||||
#define crypto_proc_fips_exit()
|
||||
#endif
|
||||
|
||||
static void *c_start(struct seq_file *m, loff_t *pos)
|
||||
{
|
||||
down_read(&crypto_alg_sem);
|
||||
@ -148,11 +109,9 @@ static const struct file_operations proc_crypto_ops = {
|
||||
void __init crypto_init_proc(void)
|
||||
{
|
||||
proc_create("crypto", 0, NULL, &proc_crypto_ops);
|
||||
crypto_proc_fips_init();
|
||||
}
|
||||
|
||||
void __exit crypto_exit_proc(void)
|
||||
{
|
||||
crypto_proc_fips_exit();
|
||||
remove_proc_entry("crypto", NULL);
|
||||
}
|
||||
|
134
crypto/rng.c
134
crypto/rng.c
@ -4,6 +4,7 @@
|
||||
* RNG operations.
|
||||
*
|
||||
* Copyright (c) 2008 Neil Horman <nhorman@tuxdriver.com>
|
||||
* Copyright (c) 2015 Herbert Xu <herbert@gondor.apana.org.au>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
@ -24,12 +25,19 @@
|
||||
#include <linux/cryptouser.h>
|
||||
#include <net/netlink.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
static DEFINE_MUTEX(crypto_default_rng_lock);
|
||||
struct crypto_rng *crypto_default_rng;
|
||||
EXPORT_SYMBOL_GPL(crypto_default_rng);
|
||||
static int crypto_default_rng_refcnt;
|
||||
|
||||
static int rngapi_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen)
|
||||
static inline struct crypto_rng *__crypto_rng_cast(struct crypto_tfm *tfm)
|
||||
{
|
||||
return container_of(tfm, struct crypto_rng, base);
|
||||
}
|
||||
|
||||
int crypto_rng_reset(struct crypto_rng *tfm, const u8 *seed, unsigned int slen)
|
||||
{
|
||||
u8 *buf = NULL;
|
||||
int err;
|
||||
@ -43,23 +51,25 @@ static int rngapi_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen)
|
||||
seed = buf;
|
||||
}
|
||||
|
||||
err = crypto_rng_alg(tfm)->rng_reset(tfm, seed, slen);
|
||||
err = crypto_rng_alg(tfm)->seed(tfm, seed, slen);
|
||||
|
||||
kfree(buf);
|
||||
kzfree(buf);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_rng_reset);
|
||||
|
||||
static int crypto_init_rng_ops(struct crypto_tfm *tfm, u32 type, u32 mask)
|
||||
static int crypto_rng_init_tfm(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct rng_alg *alg = &tfm->__crt_alg->cra_rng;
|
||||
struct rng_tfm *ops = &tfm->crt_rng;
|
||||
|
||||
ops->rng_gen_random = alg->rng_make_random;
|
||||
ops->rng_reset = rngapi_reset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int seedsize(struct crypto_alg *alg)
|
||||
{
|
||||
struct rng_alg *ralg = container_of(alg, struct rng_alg, base);
|
||||
|
||||
return ralg->seedsize;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET
|
||||
static int crypto_rng_report(struct sk_buff *skb, struct crypto_alg *alg)
|
||||
{
|
||||
@ -67,7 +77,7 @@ static int crypto_rng_report(struct sk_buff *skb, struct crypto_alg *alg)
|
||||
|
||||
strncpy(rrng.type, "rng", sizeof(rrng.type));
|
||||
|
||||
rrng.seedsize = alg->cra_rng.seedsize;
|
||||
rrng.seedsize = seedsize(alg);
|
||||
|
||||
if (nla_put(skb, CRYPTOCFGA_REPORT_RNG,
|
||||
sizeof(struct crypto_report_rng), &rrng))
|
||||
@ -89,24 +99,27 @@ static void crypto_rng_show(struct seq_file *m, struct crypto_alg *alg)
|
||||
static void crypto_rng_show(struct seq_file *m, struct crypto_alg *alg)
|
||||
{
|
||||
seq_printf(m, "type : rng\n");
|
||||
seq_printf(m, "seedsize : %u\n", alg->cra_rng.seedsize);
|
||||
seq_printf(m, "seedsize : %u\n", seedsize(alg));
|
||||
}
|
||||
|
||||
static unsigned int crypto_rng_ctxsize(struct crypto_alg *alg, u32 type,
|
||||
u32 mask)
|
||||
{
|
||||
return alg->cra_ctxsize;
|
||||
}
|
||||
|
||||
const struct crypto_type crypto_rng_type = {
|
||||
.ctxsize = crypto_rng_ctxsize,
|
||||
.init = crypto_init_rng_ops,
|
||||
static const struct crypto_type crypto_rng_type = {
|
||||
.extsize = crypto_alg_extsize,
|
||||
.init_tfm = crypto_rng_init_tfm,
|
||||
#ifdef CONFIG_PROC_FS
|
||||
.show = crypto_rng_show,
|
||||
#endif
|
||||
.report = crypto_rng_report,
|
||||
.maskclear = ~CRYPTO_ALG_TYPE_MASK,
|
||||
.maskset = CRYPTO_ALG_TYPE_MASK,
|
||||
.type = CRYPTO_ALG_TYPE_RNG,
|
||||
.tfmsize = offsetof(struct crypto_rng, base),
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(crypto_rng_type);
|
||||
|
||||
struct crypto_rng *crypto_alloc_rng(const char *alg_name, u32 type, u32 mask)
|
||||
{
|
||||
return crypto_alloc_tfm(alg_name, &crypto_rng_type, type, mask);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_alloc_rng);
|
||||
|
||||
int crypto_get_default_rng(void)
|
||||
{
|
||||
@ -142,13 +155,82 @@ EXPORT_SYMBOL_GPL(crypto_get_default_rng);
|
||||
void crypto_put_default_rng(void)
|
||||
{
|
||||
mutex_lock(&crypto_default_rng_lock);
|
||||
if (!--crypto_default_rng_refcnt) {
|
||||
crypto_free_rng(crypto_default_rng);
|
||||
crypto_default_rng = NULL;
|
||||
}
|
||||
crypto_default_rng_refcnt--;
|
||||
mutex_unlock(&crypto_default_rng_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_put_default_rng);
|
||||
|
||||
#if defined(CONFIG_CRYPTO_RNG) || defined(CONFIG_CRYPTO_RNG_MODULE)
|
||||
int crypto_del_default_rng(void)
|
||||
{
|
||||
int err = -EBUSY;
|
||||
|
||||
mutex_lock(&crypto_default_rng_lock);
|
||||
if (crypto_default_rng_refcnt)
|
||||
goto out;
|
||||
|
||||
crypto_free_rng(crypto_default_rng);
|
||||
crypto_default_rng = NULL;
|
||||
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
mutex_unlock(&crypto_default_rng_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_del_default_rng);
|
||||
#endif
|
||||
|
||||
int crypto_register_rng(struct rng_alg *alg)
|
||||
{
|
||||
struct crypto_alg *base = &alg->base;
|
||||
|
||||
if (alg->seedsize > PAGE_SIZE / 8)
|
||||
return -EINVAL;
|
||||
|
||||
base->cra_type = &crypto_rng_type;
|
||||
base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK;
|
||||
base->cra_flags |= CRYPTO_ALG_TYPE_RNG;
|
||||
|
||||
return crypto_register_alg(base);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_register_rng);
|
||||
|
||||
void crypto_unregister_rng(struct rng_alg *alg)
|
||||
{
|
||||
crypto_unregister_alg(&alg->base);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_unregister_rng);
|
||||
|
||||
int crypto_register_rngs(struct rng_alg *algs, int count)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
ret = crypto_register_rng(algs + i);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
for (--i; i >= 0; --i)
|
||||
crypto_unregister_rng(algs + i);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_register_rngs);
|
||||
|
||||
void crypto_unregister_rngs(struct rng_alg *algs, int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = count - 1; i >= 0; --i)
|
||||
crypto_unregister_rng(algs + i);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_unregister_rngs);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Random Number Generator");
|
||||
|
315
crypto/rsa.c
Normal file
315
crypto/rsa.c
Normal file
@ -0,0 +1,315 @@
|
||||
/* RSA asymmetric public-key algorithm [RFC3447]
|
||||
*
|
||||
* Copyright (c) 2015, Intel Corporation
|
||||
* Authors: Tadeusz Struk <tadeusz.struk@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <crypto/internal/rsa.h>
|
||||
#include <crypto/internal/akcipher.h>
|
||||
#include <crypto/akcipher.h>
|
||||
|
||||
/*
|
||||
* RSAEP function [RFC3447 sec 5.1.1]
|
||||
* c = m^e mod n;
|
||||
*/
|
||||
static int _rsa_enc(const struct rsa_key *key, MPI c, MPI m)
|
||||
{
|
||||
/* (1) Validate 0 <= m < n */
|
||||
if (mpi_cmp_ui(m, 0) < 0 || mpi_cmp(m, key->n) >= 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* (2) c = m^e mod n */
|
||||
return mpi_powm(c, m, key->e, key->n);
|
||||
}
|
||||
|
||||
/*
|
||||
* RSADP function [RFC3447 sec 5.1.2]
|
||||
* m = c^d mod n;
|
||||
*/
|
||||
static int _rsa_dec(const struct rsa_key *key, MPI m, MPI c)
|
||||
{
|
||||
/* (1) Validate 0 <= c < n */
|
||||
if (mpi_cmp_ui(c, 0) < 0 || mpi_cmp(c, key->n) >= 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* (2) m = c^d mod n */
|
||||
return mpi_powm(m, c, key->d, key->n);
|
||||
}
|
||||
|
||||
/*
|
||||
* RSASP1 function [RFC3447 sec 5.2.1]
|
||||
* s = m^d mod n
|
||||
*/
|
||||
static int _rsa_sign(const struct rsa_key *key, MPI s, MPI m)
|
||||
{
|
||||
/* (1) Validate 0 <= m < n */
|
||||
if (mpi_cmp_ui(m, 0) < 0 || mpi_cmp(m, key->n) >= 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* (2) s = m^d mod n */
|
||||
return mpi_powm(s, m, key->d, key->n);
|
||||
}
|
||||
|
||||
/*
|
||||
* RSAVP1 function [RFC3447 sec 5.2.2]
|
||||
* m = s^e mod n;
|
||||
*/
|
||||
static int _rsa_verify(const struct rsa_key *key, MPI m, MPI s)
|
||||
{
|
||||
/* (1) Validate 0 <= s < n */
|
||||
if (mpi_cmp_ui(s, 0) < 0 || mpi_cmp(s, key->n) >= 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* (2) m = s^e mod n */
|
||||
return mpi_powm(m, s, key->e, key->n);
|
||||
}
|
||||
|
||||
static inline struct rsa_key *rsa_get_key(struct crypto_akcipher *tfm)
|
||||
{
|
||||
return akcipher_tfm_ctx(tfm);
|
||||
}
|
||||
|
||||
static int rsa_enc(struct akcipher_request *req)
|
||||
{
|
||||
struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
|
||||
const struct rsa_key *pkey = rsa_get_key(tfm);
|
||||
MPI m, c = mpi_alloc(0);
|
||||
int ret = 0;
|
||||
int sign;
|
||||
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
if (unlikely(!pkey->n || !pkey->e)) {
|
||||
ret = -EINVAL;
|
||||
goto err_free_c;
|
||||
}
|
||||
|
||||
if (req->dst_len < mpi_get_size(pkey->n)) {
|
||||
req->dst_len = mpi_get_size(pkey->n);
|
||||
ret = -EOVERFLOW;
|
||||
goto err_free_c;
|
||||
}
|
||||
|
||||
m = mpi_read_raw_data(req->src, req->src_len);
|
||||
if (!m) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_c;
|
||||
}
|
||||
|
||||
ret = _rsa_enc(pkey, c, m);
|
||||
if (ret)
|
||||
goto err_free_m;
|
||||
|
||||
ret = mpi_read_buffer(c, req->dst, req->dst_len, &req->dst_len, &sign);
|
||||
if (ret)
|
||||
goto err_free_m;
|
||||
|
||||
if (sign < 0) {
|
||||
ret = -EBADMSG;
|
||||
goto err_free_m;
|
||||
}
|
||||
|
||||
err_free_m:
|
||||
mpi_free(m);
|
||||
err_free_c:
|
||||
mpi_free(c);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rsa_dec(struct akcipher_request *req)
|
||||
{
|
||||
struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
|
||||
const struct rsa_key *pkey = rsa_get_key(tfm);
|
||||
MPI c, m = mpi_alloc(0);
|
||||
int ret = 0;
|
||||
int sign;
|
||||
|
||||
if (!m)
|
||||
return -ENOMEM;
|
||||
|
||||
if (unlikely(!pkey->n || !pkey->d)) {
|
||||
ret = -EINVAL;
|
||||
goto err_free_m;
|
||||
}
|
||||
|
||||
if (req->dst_len < mpi_get_size(pkey->n)) {
|
||||
req->dst_len = mpi_get_size(pkey->n);
|
||||
ret = -EOVERFLOW;
|
||||
goto err_free_m;
|
||||
}
|
||||
|
||||
c = mpi_read_raw_data(req->src, req->src_len);
|
||||
if (!c) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_m;
|
||||
}
|
||||
|
||||
ret = _rsa_dec(pkey, m, c);
|
||||
if (ret)
|
||||
goto err_free_c;
|
||||
|
||||
ret = mpi_read_buffer(m, req->dst, req->dst_len, &req->dst_len, &sign);
|
||||
if (ret)
|
||||
goto err_free_c;
|
||||
|
||||
if (sign < 0) {
|
||||
ret = -EBADMSG;
|
||||
goto err_free_c;
|
||||
}
|
||||
|
||||
err_free_c:
|
||||
mpi_free(c);
|
||||
err_free_m:
|
||||
mpi_free(m);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rsa_sign(struct akcipher_request *req)
|
||||
{
|
||||
struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
|
||||
const struct rsa_key *pkey = rsa_get_key(tfm);
|
||||
MPI m, s = mpi_alloc(0);
|
||||
int ret = 0;
|
||||
int sign;
|
||||
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
if (unlikely(!pkey->n || !pkey->d)) {
|
||||
ret = -EINVAL;
|
||||
goto err_free_s;
|
||||
}
|
||||
|
||||
if (req->dst_len < mpi_get_size(pkey->n)) {
|
||||
req->dst_len = mpi_get_size(pkey->n);
|
||||
ret = -EOVERFLOW;
|
||||
goto err_free_s;
|
||||
}
|
||||
|
||||
m = mpi_read_raw_data(req->src, req->src_len);
|
||||
if (!m) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_s;
|
||||
}
|
||||
|
||||
ret = _rsa_sign(pkey, s, m);
|
||||
if (ret)
|
||||
goto err_free_m;
|
||||
|
||||
ret = mpi_read_buffer(s, req->dst, req->dst_len, &req->dst_len, &sign);
|
||||
if (ret)
|
||||
goto err_free_m;
|
||||
|
||||
if (sign < 0) {
|
||||
ret = -EBADMSG;
|
||||
goto err_free_m;
|
||||
}
|
||||
|
||||
err_free_m:
|
||||
mpi_free(m);
|
||||
err_free_s:
|
||||
mpi_free(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rsa_verify(struct akcipher_request *req)
|
||||
{
|
||||
struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
|
||||
const struct rsa_key *pkey = rsa_get_key(tfm);
|
||||
MPI s, m = mpi_alloc(0);
|
||||
int ret = 0;
|
||||
int sign;
|
||||
|
||||
if (!m)
|
||||
return -ENOMEM;
|
||||
|
||||
if (unlikely(!pkey->n || !pkey->e)) {
|
||||
ret = -EINVAL;
|
||||
goto err_free_m;
|
||||
}
|
||||
|
||||
if (req->dst_len < mpi_get_size(pkey->n)) {
|
||||
req->dst_len = mpi_get_size(pkey->n);
|
||||
ret = -EOVERFLOW;
|
||||
goto err_free_m;
|
||||
}
|
||||
|
||||
s = mpi_read_raw_data(req->src, req->src_len);
|
||||
if (!s) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_m;
|
||||
}
|
||||
|
||||
ret = _rsa_verify(pkey, m, s);
|
||||
if (ret)
|
||||
goto err_free_s;
|
||||
|
||||
ret = mpi_read_buffer(m, req->dst, req->dst_len, &req->dst_len, &sign);
|
||||
if (ret)
|
||||
goto err_free_s;
|
||||
|
||||
if (sign < 0) {
|
||||
ret = -EBADMSG;
|
||||
goto err_free_s;
|
||||
}
|
||||
|
||||
err_free_s:
|
||||
mpi_free(s);
|
||||
err_free_m:
|
||||
mpi_free(m);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rsa_setkey(struct crypto_akcipher *tfm, const void *key,
|
||||
unsigned int keylen)
|
||||
{
|
||||
struct rsa_key *pkey = akcipher_tfm_ctx(tfm);
|
||||
|
||||
return rsa_parse_key(pkey, key, keylen);
|
||||
}
|
||||
|
||||
static void rsa_exit_tfm(struct crypto_akcipher *tfm)
|
||||
{
|
||||
struct rsa_key *pkey = akcipher_tfm_ctx(tfm);
|
||||
|
||||
rsa_free_key(pkey);
|
||||
}
|
||||
|
||||
static struct akcipher_alg rsa = {
|
||||
.encrypt = rsa_enc,
|
||||
.decrypt = rsa_dec,
|
||||
.sign = rsa_sign,
|
||||
.verify = rsa_verify,
|
||||
.setkey = rsa_setkey,
|
||||
.exit = rsa_exit_tfm,
|
||||
.base = {
|
||||
.cra_name = "rsa",
|
||||
.cra_driver_name = "rsa-generic",
|
||||
.cra_priority = 100,
|
||||
.cra_module = THIS_MODULE,
|
||||
.cra_ctxsize = sizeof(struct rsa_key),
|
||||
},
|
||||
};
|
||||
|
||||
static int rsa_init(void)
|
||||
{
|
||||
return crypto_register_akcipher(&rsa);
|
||||
}
|
||||
|
||||
static void rsa_exit(void)
|
||||
{
|
||||
crypto_unregister_akcipher(&rsa);
|
||||
}
|
||||
|
||||
module_init(rsa_init);
|
||||
module_exit(rsa_exit);
|
||||
MODULE_ALIAS_CRYPTO("rsa");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("RSA generic algorithm");
|
121
crypto/rsa_helper.c
Normal file
121
crypto/rsa_helper.c
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* RSA key extract helper
|
||||
*
|
||||
* Copyright (c) 2015, Intel Corporation
|
||||
* Authors: Tadeusz Struk <tadeusz.struk@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/fips.h>
|
||||
#include <crypto/internal/rsa.h>
|
||||
#include "rsakey-asn1.h"
|
||||
|
||||
int rsa_get_n(void *context, size_t hdrlen, unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct rsa_key *key = context;
|
||||
|
||||
key->n = mpi_read_raw_data(value, vlen);
|
||||
|
||||
if (!key->n)
|
||||
return -ENOMEM;
|
||||
|
||||
/* In FIPS mode only allow key size 2K & 3K */
|
||||
if (fips_enabled && (mpi_get_size(key->n) != 256 ||
|
||||
mpi_get_size(key->n) != 384)) {
|
||||
pr_err("RSA: key size not allowed in FIPS mode\n");
|
||||
mpi_free(key->n);
|
||||
key->n = NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsa_get_e(void *context, size_t hdrlen, unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct rsa_key *key = context;
|
||||
|
||||
key->e = mpi_read_raw_data(value, vlen);
|
||||
|
||||
if (!key->e)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsa_get_d(void *context, size_t hdrlen, unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct rsa_key *key = context;
|
||||
|
||||
key->d = mpi_read_raw_data(value, vlen);
|
||||
|
||||
if (!key->d)
|
||||
return -ENOMEM;
|
||||
|
||||
/* In FIPS mode only allow key size 2K & 3K */
|
||||
if (fips_enabled && (mpi_get_size(key->d) != 256 ||
|
||||
mpi_get_size(key->d) != 384)) {
|
||||
pr_err("RSA: key size not allowed in FIPS mode\n");
|
||||
mpi_free(key->d);
|
||||
key->d = NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_mpis(struct rsa_key *key)
|
||||
{
|
||||
mpi_free(key->n);
|
||||
mpi_free(key->e);
|
||||
mpi_free(key->d);
|
||||
key->n = NULL;
|
||||
key->e = NULL;
|
||||
key->d = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* rsa_free_key() - frees rsa key allocated by rsa_parse_key()
|
||||
*
|
||||
* @rsa_key: struct rsa_key key representation
|
||||
*/
|
||||
void rsa_free_key(struct rsa_key *key)
|
||||
{
|
||||
free_mpis(key);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rsa_free_key);
|
||||
|
||||
/**
|
||||
* rsa_parse_key() - extracts an rsa key from BER encoded buffer
|
||||
* and stores it in the provided struct rsa_key
|
||||
*
|
||||
* @rsa_key: struct rsa_key key representation
|
||||
* @key: key in BER format
|
||||
* @key_len: length of key
|
||||
*
|
||||
* Return: 0 on success or error code in case of error
|
||||
*/
|
||||
int rsa_parse_key(struct rsa_key *rsa_key, const void *key,
|
||||
unsigned int key_len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
free_mpis(rsa_key);
|
||||
ret = asn1_ber_decoder(&rsakey_decoder, rsa_key, key, key_len);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
error:
|
||||
free_mpis(rsa_key);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rsa_parse_key);
|
5
crypto/rsakey.asn1
Normal file
5
crypto/rsakey.asn1
Normal file
@ -0,0 +1,5 @@
|
||||
RsaKey ::= SEQUENCE {
|
||||
n INTEGER ({ rsa_get_n }),
|
||||
e INTEGER ({ rsa_get_e }),
|
||||
d INTEGER ({ rsa_get_d })
|
||||
}
|
@ -54,7 +54,11 @@ static void scatterwalk_pagedone(struct scatter_walk *walk, int out,
|
||||
struct page *page;
|
||||
|
||||
page = sg_page(walk->sg) + ((walk->offset - 1) >> PAGE_SHIFT);
|
||||
if (!PageSlab(page))
|
||||
/* Test ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE first as
|
||||
* PageSlab cannot be optimised away per se due to
|
||||
* use of volatile pointer.
|
||||
*/
|
||||
if (ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE && !PageSlab(page))
|
||||
flush_dcache_page(page);
|
||||
}
|
||||
|
||||
@ -104,22 +108,18 @@ void scatterwalk_map_and_copy(void *buf, struct scatterlist *sg,
|
||||
unsigned int start, unsigned int nbytes, int out)
|
||||
{
|
||||
struct scatter_walk walk;
|
||||
unsigned int offset = 0;
|
||||
struct scatterlist tmp[2];
|
||||
|
||||
if (!nbytes)
|
||||
return;
|
||||
|
||||
for (;;) {
|
||||
scatterwalk_start(&walk, sg);
|
||||
sg = scatterwalk_ffwd(tmp, sg, start);
|
||||
|
||||
if (start < offset + sg->length)
|
||||
break;
|
||||
if (sg_page(sg) == virt_to_page(buf) &&
|
||||
sg->offset == offset_in_page(buf))
|
||||
return;
|
||||
|
||||
offset += sg->length;
|
||||
sg = sg_next(sg);
|
||||
}
|
||||
|
||||
scatterwalk_advance(&walk, start - offset);
|
||||
scatterwalk_start(&walk, sg);
|
||||
scatterwalk_copychunks(buf, &walk, nbytes, out);
|
||||
scatterwalk_done(&walk, out, 0);
|
||||
}
|
||||
@ -146,3 +146,26 @@ int scatterwalk_bytes_sglen(struct scatterlist *sg, int num_bytes)
|
||||
return n;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scatterwalk_bytes_sglen);
|
||||
|
||||
struct scatterlist *scatterwalk_ffwd(struct scatterlist dst[2],
|
||||
struct scatterlist *src,
|
||||
unsigned int len)
|
||||
{
|
||||
for (;;) {
|
||||
if (!len)
|
||||
return src;
|
||||
|
||||
if (src->length > len)
|
||||
break;
|
||||
|
||||
len -= src->length;
|
||||
src = sg_next(src);
|
||||
}
|
||||
|
||||
sg_init_table(dst, 2);
|
||||
sg_set_page(dst, sg_page(src), src->length - len, src->offset + len);
|
||||
scatterwalk_crypto_chain(dst, sg_next(src), 0, 2);
|
||||
|
||||
return dst;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scatterwalk_ffwd);
|
||||
|
601
crypto/seqiv.c
601
crypto/seqiv.c
@ -13,9 +13,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <crypto/internal/aead.h>
|
||||
#include <crypto/internal/geniv.h>
|
||||
#include <crypto/internal/skcipher.h>
|
||||
#include <crypto/null.h>
|
||||
#include <crypto/rng.h>
|
||||
#include <crypto/scatterwalk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
@ -24,11 +26,25 @@
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
struct seqniv_request_ctx {
|
||||
struct scatterlist dst[2];
|
||||
struct aead_request subreq;
|
||||
};
|
||||
|
||||
struct seqiv_ctx {
|
||||
spinlock_t lock;
|
||||
u8 salt[] __attribute__ ((aligned(__alignof__(u32))));
|
||||
};
|
||||
|
||||
struct seqiv_aead_ctx {
|
||||
/* aead_geniv_ctx must be first the element */
|
||||
struct aead_geniv_ctx geniv;
|
||||
struct crypto_blkcipher *null;
|
||||
u8 salt[] __attribute__ ((aligned(__alignof__(u32))));
|
||||
};
|
||||
|
||||
static void seqiv_free(struct crypto_instance *inst);
|
||||
|
||||
static void seqiv_complete2(struct skcipher_givcrypt_request *req, int err)
|
||||
{
|
||||
struct ablkcipher_request *subreq = skcipher_givcrypt_reqctx(req);
|
||||
@ -81,6 +97,77 @@ static void seqiv_aead_complete(struct crypto_async_request *base, int err)
|
||||
aead_givcrypt_complete(req, err);
|
||||
}
|
||||
|
||||
static void seqiv_aead_encrypt_complete2(struct aead_request *req, int err)
|
||||
{
|
||||
struct aead_request *subreq = aead_request_ctx(req);
|
||||
struct crypto_aead *geniv;
|
||||
|
||||
if (err == -EINPROGRESS)
|
||||
return;
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
geniv = crypto_aead_reqtfm(req);
|
||||
memcpy(req->iv, subreq->iv, crypto_aead_ivsize(geniv));
|
||||
|
||||
out:
|
||||
kzfree(subreq->iv);
|
||||
}
|
||||
|
||||
static void seqiv_aead_encrypt_complete(struct crypto_async_request *base,
|
||||
int err)
|
||||
{
|
||||
struct aead_request *req = base->data;
|
||||
|
||||
seqiv_aead_encrypt_complete2(req, err);
|
||||
aead_request_complete(req, err);
|
||||
}
|
||||
|
||||
static void seqniv_aead_encrypt_complete2(struct aead_request *req, int err)
|
||||
{
|
||||
unsigned int ivsize = 8;
|
||||
u8 data[20];
|
||||
|
||||
if (err == -EINPROGRESS)
|
||||
return;
|
||||
|
||||
/* Swap IV and ESP header back to correct order. */
|
||||
scatterwalk_map_and_copy(data, req->dst, 0, req->assoclen + ivsize, 0);
|
||||
scatterwalk_map_and_copy(data + ivsize, req->dst, 0, req->assoclen, 1);
|
||||
scatterwalk_map_and_copy(data, req->dst, req->assoclen, ivsize, 1);
|
||||
}
|
||||
|
||||
static void seqniv_aead_encrypt_complete(struct crypto_async_request *base,
|
||||
int err)
|
||||
{
|
||||
struct aead_request *req = base->data;
|
||||
|
||||
seqniv_aead_encrypt_complete2(req, err);
|
||||
aead_request_complete(req, err);
|
||||
}
|
||||
|
||||
static void seqniv_aead_decrypt_complete2(struct aead_request *req, int err)
|
||||
{
|
||||
u8 data[4];
|
||||
|
||||
if (err == -EINPROGRESS)
|
||||
return;
|
||||
|
||||
/* Move ESP header back to correct location. */
|
||||
scatterwalk_map_and_copy(data, req->dst, 16, req->assoclen - 8, 0);
|
||||
scatterwalk_map_and_copy(data, req->dst, 8, req->assoclen - 8, 1);
|
||||
}
|
||||
|
||||
static void seqniv_aead_decrypt_complete(struct crypto_async_request *base,
|
||||
int err)
|
||||
{
|
||||
struct aead_request *req = base->data;
|
||||
|
||||
seqniv_aead_decrypt_complete2(req, err);
|
||||
aead_request_complete(req, err);
|
||||
}
|
||||
|
||||
static void seqiv_geniv(struct seqiv_ctx *ctx, u8 *info, u64 seq,
|
||||
unsigned int ivsize)
|
||||
{
|
||||
@ -186,160 +273,477 @@ static int seqiv_aead_givencrypt(struct aead_givcrypt_request *req)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int seqiv_givencrypt_first(struct skcipher_givcrypt_request *req)
|
||||
static int seqniv_aead_encrypt(struct aead_request *req)
|
||||
{
|
||||
struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req);
|
||||
struct seqiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
|
||||
int err = 0;
|
||||
struct crypto_aead *geniv = crypto_aead_reqtfm(req);
|
||||
struct seqiv_aead_ctx *ctx = crypto_aead_ctx(geniv);
|
||||
struct seqniv_request_ctx *rctx = aead_request_ctx(req);
|
||||
struct aead_request *subreq = &rctx->subreq;
|
||||
struct scatterlist *dst;
|
||||
crypto_completion_t compl;
|
||||
void *data;
|
||||
unsigned int ivsize = 8;
|
||||
u8 buf[20] __attribute__ ((aligned(__alignof__(u32))));
|
||||
int err;
|
||||
|
||||
spin_lock_bh(&ctx->lock);
|
||||
if (crypto_ablkcipher_crt(geniv)->givencrypt != seqiv_givencrypt_first)
|
||||
goto unlock;
|
||||
if (req->cryptlen < ivsize)
|
||||
return -EINVAL;
|
||||
|
||||
crypto_ablkcipher_crt(geniv)->givencrypt = seqiv_givencrypt;
|
||||
err = crypto_rng_get_bytes(crypto_default_rng, ctx->salt,
|
||||
crypto_ablkcipher_ivsize(geniv));
|
||||
/* ESP AD is at most 12 bytes (ESN). */
|
||||
if (req->assoclen > 12)
|
||||
return -EINVAL;
|
||||
|
||||
unlock:
|
||||
spin_unlock_bh(&ctx->lock);
|
||||
aead_request_set_tfm(subreq, ctx->geniv.child);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
compl = seqniv_aead_encrypt_complete;
|
||||
data = req;
|
||||
|
||||
return seqiv_givencrypt(req);
|
||||
if (req->src != req->dst) {
|
||||
struct blkcipher_desc desc = {
|
||||
.tfm = ctx->null,
|
||||
};
|
||||
|
||||
err = crypto_blkcipher_encrypt(&desc, req->dst, req->src,
|
||||
req->assoclen + req->cryptlen);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
dst = scatterwalk_ffwd(rctx->dst, req->dst, ivsize);
|
||||
|
||||
aead_request_set_callback(subreq, req->base.flags, compl, data);
|
||||
aead_request_set_crypt(subreq, dst, dst,
|
||||
req->cryptlen - ivsize, req->iv);
|
||||
aead_request_set_ad(subreq, req->assoclen);
|
||||
|
||||
memcpy(buf, req->iv, ivsize);
|
||||
crypto_xor(buf, ctx->salt, ivsize);
|
||||
memcpy(req->iv, buf, ivsize);
|
||||
|
||||
/* Swap order of IV and ESP AD for ICV generation. */
|
||||
scatterwalk_map_and_copy(buf + ivsize, req->dst, 0, req->assoclen, 0);
|
||||
scatterwalk_map_and_copy(buf, req->dst, 0, req->assoclen + ivsize, 1);
|
||||
|
||||
err = crypto_aead_encrypt(subreq);
|
||||
seqniv_aead_encrypt_complete2(req, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int seqiv_aead_givencrypt_first(struct aead_givcrypt_request *req)
|
||||
static int seqiv_aead_encrypt(struct aead_request *req)
|
||||
{
|
||||
struct crypto_aead *geniv = aead_givcrypt_reqtfm(req);
|
||||
struct seqiv_ctx *ctx = crypto_aead_ctx(geniv);
|
||||
int err = 0;
|
||||
struct crypto_aead *geniv = crypto_aead_reqtfm(req);
|
||||
struct seqiv_aead_ctx *ctx = crypto_aead_ctx(geniv);
|
||||
struct aead_request *subreq = aead_request_ctx(req);
|
||||
crypto_completion_t compl;
|
||||
void *data;
|
||||
u8 *info;
|
||||
unsigned int ivsize = 8;
|
||||
int err;
|
||||
|
||||
spin_lock_bh(&ctx->lock);
|
||||
if (crypto_aead_crt(geniv)->givencrypt != seqiv_aead_givencrypt_first)
|
||||
goto unlock;
|
||||
if (req->cryptlen < ivsize)
|
||||
return -EINVAL;
|
||||
|
||||
crypto_aead_crt(geniv)->givencrypt = seqiv_aead_givencrypt;
|
||||
err = crypto_rng_get_bytes(crypto_default_rng, ctx->salt,
|
||||
crypto_aead_ivsize(geniv));
|
||||
aead_request_set_tfm(subreq, ctx->geniv.child);
|
||||
|
||||
unlock:
|
||||
spin_unlock_bh(&ctx->lock);
|
||||
compl = req->base.complete;
|
||||
data = req->base.data;
|
||||
info = req->iv;
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
if (req->src != req->dst) {
|
||||
struct blkcipher_desc desc = {
|
||||
.tfm = ctx->null,
|
||||
};
|
||||
|
||||
return seqiv_aead_givencrypt(req);
|
||||
err = crypto_blkcipher_encrypt(&desc, req->dst, req->src,
|
||||
req->assoclen + req->cryptlen);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (unlikely(!IS_ALIGNED((unsigned long)info,
|
||||
crypto_aead_alignmask(geniv) + 1))) {
|
||||
info = kmalloc(ivsize, req->base.flags &
|
||||
CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL:
|
||||
GFP_ATOMIC);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(info, req->iv, ivsize);
|
||||
compl = seqiv_aead_encrypt_complete;
|
||||
data = req;
|
||||
}
|
||||
|
||||
aead_request_set_callback(subreq, req->base.flags, compl, data);
|
||||
aead_request_set_crypt(subreq, req->dst, req->dst,
|
||||
req->cryptlen - ivsize, info);
|
||||
aead_request_set_ad(subreq, req->assoclen + ivsize);
|
||||
|
||||
crypto_xor(info, ctx->salt, ivsize);
|
||||
scatterwalk_map_and_copy(info, req->dst, req->assoclen, ivsize, 1);
|
||||
|
||||
err = crypto_aead_encrypt(subreq);
|
||||
if (unlikely(info != req->iv))
|
||||
seqiv_aead_encrypt_complete2(req, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int seqniv_aead_decrypt(struct aead_request *req)
|
||||
{
|
||||
struct crypto_aead *geniv = crypto_aead_reqtfm(req);
|
||||
struct seqiv_aead_ctx *ctx = crypto_aead_ctx(geniv);
|
||||
struct seqniv_request_ctx *rctx = aead_request_ctx(req);
|
||||
struct aead_request *subreq = &rctx->subreq;
|
||||
struct scatterlist *dst;
|
||||
crypto_completion_t compl;
|
||||
void *data;
|
||||
unsigned int ivsize = 8;
|
||||
u8 buf[20];
|
||||
int err;
|
||||
|
||||
if (req->cryptlen < ivsize + crypto_aead_authsize(geniv))
|
||||
return -EINVAL;
|
||||
|
||||
aead_request_set_tfm(subreq, ctx->geniv.child);
|
||||
|
||||
compl = req->base.complete;
|
||||
data = req->base.data;
|
||||
|
||||
if (req->assoclen > 12)
|
||||
return -EINVAL;
|
||||
else if (req->assoclen > 8) {
|
||||
compl = seqniv_aead_decrypt_complete;
|
||||
data = req;
|
||||
}
|
||||
|
||||
if (req->src != req->dst) {
|
||||
struct blkcipher_desc desc = {
|
||||
.tfm = ctx->null,
|
||||
};
|
||||
|
||||
err = crypto_blkcipher_encrypt(&desc, req->dst, req->src,
|
||||
req->assoclen + req->cryptlen);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Move ESP AD forward for ICV generation. */
|
||||
scatterwalk_map_and_copy(buf, req->dst, 0, req->assoclen + ivsize, 0);
|
||||
memcpy(req->iv, buf + req->assoclen, ivsize);
|
||||
scatterwalk_map_and_copy(buf, req->dst, ivsize, req->assoclen, 1);
|
||||
|
||||
dst = scatterwalk_ffwd(rctx->dst, req->dst, ivsize);
|
||||
|
||||
aead_request_set_callback(subreq, req->base.flags, compl, data);
|
||||
aead_request_set_crypt(subreq, dst, dst,
|
||||
req->cryptlen - ivsize, req->iv);
|
||||
aead_request_set_ad(subreq, req->assoclen);
|
||||
|
||||
err = crypto_aead_decrypt(subreq);
|
||||
if (req->assoclen > 8)
|
||||
seqniv_aead_decrypt_complete2(req, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int seqiv_aead_decrypt(struct aead_request *req)
|
||||
{
|
||||
struct crypto_aead *geniv = crypto_aead_reqtfm(req);
|
||||
struct seqiv_aead_ctx *ctx = crypto_aead_ctx(geniv);
|
||||
struct aead_request *subreq = aead_request_ctx(req);
|
||||
crypto_completion_t compl;
|
||||
void *data;
|
||||
unsigned int ivsize = 8;
|
||||
|
||||
if (req->cryptlen < ivsize + crypto_aead_authsize(geniv))
|
||||
return -EINVAL;
|
||||
|
||||
aead_request_set_tfm(subreq, ctx->geniv.child);
|
||||
|
||||
compl = req->base.complete;
|
||||
data = req->base.data;
|
||||
|
||||
aead_request_set_callback(subreq, req->base.flags, compl, data);
|
||||
aead_request_set_crypt(subreq, req->src, req->dst,
|
||||
req->cryptlen - ivsize, req->iv);
|
||||
aead_request_set_ad(subreq, req->assoclen + ivsize);
|
||||
|
||||
scatterwalk_map_and_copy(req->iv, req->src, req->assoclen, ivsize, 0);
|
||||
if (req->src != req->dst)
|
||||
scatterwalk_map_and_copy(req->iv, req->dst,
|
||||
req->assoclen, ivsize, 1);
|
||||
|
||||
return crypto_aead_decrypt(subreq);
|
||||
}
|
||||
|
||||
static int seqiv_init(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct crypto_ablkcipher *geniv = __crypto_ablkcipher_cast(tfm);
|
||||
struct seqiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
|
||||
int err;
|
||||
|
||||
spin_lock_init(&ctx->lock);
|
||||
|
||||
tfm->crt_ablkcipher.reqsize = sizeof(struct ablkcipher_request);
|
||||
|
||||
return skcipher_geniv_init(tfm);
|
||||
err = 0;
|
||||
if (!crypto_get_default_rng()) {
|
||||
crypto_ablkcipher_crt(geniv)->givencrypt = seqiv_givencrypt;
|
||||
err = crypto_rng_get_bytes(crypto_default_rng, ctx->salt,
|
||||
crypto_ablkcipher_ivsize(geniv));
|
||||
crypto_put_default_rng();
|
||||
}
|
||||
|
||||
return err ?: skcipher_geniv_init(tfm);
|
||||
}
|
||||
|
||||
static int seqiv_old_aead_init(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct crypto_aead *geniv = __crypto_aead_cast(tfm);
|
||||
struct seqiv_ctx *ctx = crypto_aead_ctx(geniv);
|
||||
int err;
|
||||
|
||||
spin_lock_init(&ctx->lock);
|
||||
|
||||
crypto_aead_set_reqsize(__crypto_aead_cast(tfm),
|
||||
sizeof(struct aead_request));
|
||||
err = 0;
|
||||
if (!crypto_get_default_rng()) {
|
||||
geniv->givencrypt = seqiv_aead_givencrypt;
|
||||
err = crypto_rng_get_bytes(crypto_default_rng, ctx->salt,
|
||||
crypto_aead_ivsize(geniv));
|
||||
crypto_put_default_rng();
|
||||
}
|
||||
|
||||
return err ?: aead_geniv_init(tfm);
|
||||
}
|
||||
|
||||
static int seqiv_aead_init_common(struct crypto_tfm *tfm, unsigned int reqsize)
|
||||
{
|
||||
struct crypto_aead *geniv = __crypto_aead_cast(tfm);
|
||||
struct seqiv_aead_ctx *ctx = crypto_aead_ctx(geniv);
|
||||
int err;
|
||||
|
||||
spin_lock_init(&ctx->geniv.lock);
|
||||
|
||||
crypto_aead_set_reqsize(geniv, sizeof(struct aead_request));
|
||||
|
||||
err = crypto_get_default_rng();
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = crypto_rng_get_bytes(crypto_default_rng, ctx->salt,
|
||||
crypto_aead_ivsize(geniv));
|
||||
crypto_put_default_rng();
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
ctx->null = crypto_get_default_null_skcipher();
|
||||
err = PTR_ERR(ctx->null);
|
||||
if (IS_ERR(ctx->null))
|
||||
goto out;
|
||||
|
||||
err = aead_geniv_init(tfm);
|
||||
if (err)
|
||||
goto drop_null;
|
||||
|
||||
ctx->geniv.child = geniv->child;
|
||||
geniv->child = geniv;
|
||||
|
||||
out:
|
||||
return err;
|
||||
|
||||
drop_null:
|
||||
crypto_put_default_null_skcipher();
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int seqiv_aead_init(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct crypto_aead *geniv = __crypto_aead_cast(tfm);
|
||||
struct seqiv_ctx *ctx = crypto_aead_ctx(geniv);
|
||||
|
||||
spin_lock_init(&ctx->lock);
|
||||
|
||||
tfm->crt_aead.reqsize = sizeof(struct aead_request);
|
||||
|
||||
return aead_geniv_init(tfm);
|
||||
return seqiv_aead_init_common(tfm, sizeof(struct aead_request));
|
||||
}
|
||||
|
||||
static struct crypto_template seqiv_tmpl;
|
||||
static int seqniv_aead_init(struct crypto_tfm *tfm)
|
||||
{
|
||||
return seqiv_aead_init_common(tfm, sizeof(struct seqniv_request_ctx));
|
||||
}
|
||||
|
||||
static struct crypto_instance *seqiv_ablkcipher_alloc(struct rtattr **tb)
|
||||
static void seqiv_aead_exit(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct seqiv_aead_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
|
||||
crypto_free_aead(ctx->geniv.child);
|
||||
crypto_put_default_null_skcipher();
|
||||
}
|
||||
|
||||
static int seqiv_ablkcipher_create(struct crypto_template *tmpl,
|
||||
struct rtattr **tb)
|
||||
{
|
||||
struct crypto_instance *inst;
|
||||
int err;
|
||||
|
||||
inst = skcipher_geniv_alloc(&seqiv_tmpl, tb, 0, 0);
|
||||
inst = skcipher_geniv_alloc(tmpl, tb, 0, 0);
|
||||
|
||||
if (IS_ERR(inst))
|
||||
goto out;
|
||||
return PTR_ERR(inst);
|
||||
|
||||
if (inst->alg.cra_ablkcipher.ivsize < sizeof(u64)) {
|
||||
skcipher_geniv_free(inst);
|
||||
inst = ERR_PTR(-EINVAL);
|
||||
goto out;
|
||||
}
|
||||
|
||||
inst->alg.cra_ablkcipher.givencrypt = seqiv_givencrypt_first;
|
||||
err = -EINVAL;
|
||||
if (inst->alg.cra_ablkcipher.ivsize < sizeof(u64))
|
||||
goto free_inst;
|
||||
|
||||
inst->alg.cra_init = seqiv_init;
|
||||
inst->alg.cra_exit = skcipher_geniv_exit;
|
||||
|
||||
inst->alg.cra_ctxsize += inst->alg.cra_ablkcipher.ivsize;
|
||||
inst->alg.cra_ctxsize += sizeof(struct seqiv_ctx);
|
||||
|
||||
inst->alg.cra_alignmask |= __alignof__(u32) - 1;
|
||||
|
||||
err = crypto_register_instance(tmpl, inst);
|
||||
if (err)
|
||||
goto free_inst;
|
||||
|
||||
out:
|
||||
return inst;
|
||||
return err;
|
||||
|
||||
free_inst:
|
||||
skcipher_geniv_free(inst);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static struct crypto_instance *seqiv_aead_alloc(struct rtattr **tb)
|
||||
static int seqiv_old_aead_create(struct crypto_template *tmpl,
|
||||
struct aead_instance *aead)
|
||||
{
|
||||
struct crypto_instance *inst;
|
||||
struct crypto_instance *inst = aead_crypto_instance(aead);
|
||||
int err = -EINVAL;
|
||||
|
||||
inst = aead_geniv_alloc(&seqiv_tmpl, tb, 0, 0);
|
||||
if (inst->alg.cra_aead.ivsize < sizeof(u64))
|
||||
goto free_inst;
|
||||
|
||||
if (IS_ERR(inst))
|
||||
goto out;
|
||||
|
||||
if (inst->alg.cra_aead.ivsize < sizeof(u64)) {
|
||||
aead_geniv_free(inst);
|
||||
inst = ERR_PTR(-EINVAL);
|
||||
goto out;
|
||||
}
|
||||
|
||||
inst->alg.cra_aead.givencrypt = seqiv_aead_givencrypt_first;
|
||||
|
||||
inst->alg.cra_init = seqiv_aead_init;
|
||||
inst->alg.cra_init = seqiv_old_aead_init;
|
||||
inst->alg.cra_exit = aead_geniv_exit;
|
||||
|
||||
inst->alg.cra_ctxsize = inst->alg.cra_aead.ivsize;
|
||||
inst->alg.cra_ctxsize += sizeof(struct seqiv_ctx);
|
||||
|
||||
err = crypto_register_instance(tmpl, inst);
|
||||
if (err)
|
||||
goto free_inst;
|
||||
|
||||
out:
|
||||
return inst;
|
||||
return err;
|
||||
|
||||
free_inst:
|
||||
aead_geniv_free(aead);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static struct crypto_instance *seqiv_alloc(struct rtattr **tb)
|
||||
static int seqiv_aead_create(struct crypto_template *tmpl, struct rtattr **tb)
|
||||
{
|
||||
struct aead_instance *inst;
|
||||
struct crypto_aead_spawn *spawn;
|
||||
struct aead_alg *alg;
|
||||
int err;
|
||||
|
||||
inst = aead_geniv_alloc(tmpl, tb, 0, 0);
|
||||
|
||||
if (IS_ERR(inst))
|
||||
return PTR_ERR(inst);
|
||||
|
||||
inst->alg.base.cra_alignmask |= __alignof__(u32) - 1;
|
||||
|
||||
if (inst->alg.base.cra_aead.encrypt)
|
||||
return seqiv_old_aead_create(tmpl, inst);
|
||||
|
||||
spawn = aead_instance_ctx(inst);
|
||||
alg = crypto_spawn_aead_alg(spawn);
|
||||
|
||||
if (alg->base.cra_aead.encrypt)
|
||||
goto done;
|
||||
|
||||
err = -EINVAL;
|
||||
if (inst->alg.ivsize != sizeof(u64))
|
||||
goto free_inst;
|
||||
|
||||
inst->alg.encrypt = seqiv_aead_encrypt;
|
||||
inst->alg.decrypt = seqiv_aead_decrypt;
|
||||
|
||||
inst->alg.base.cra_init = seqiv_aead_init;
|
||||
inst->alg.base.cra_exit = seqiv_aead_exit;
|
||||
|
||||
inst->alg.base.cra_ctxsize = sizeof(struct seqiv_aead_ctx);
|
||||
inst->alg.base.cra_ctxsize += inst->alg.base.cra_aead.ivsize;
|
||||
|
||||
done:
|
||||
err = aead_register_instance(tmpl, inst);
|
||||
if (err)
|
||||
goto free_inst;
|
||||
|
||||
out:
|
||||
return err;
|
||||
|
||||
free_inst:
|
||||
aead_geniv_free(inst);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int seqiv_create(struct crypto_template *tmpl, struct rtattr **tb)
|
||||
{
|
||||
struct crypto_attr_type *algt;
|
||||
struct crypto_instance *inst;
|
||||
int err;
|
||||
|
||||
algt = crypto_get_attr_type(tb);
|
||||
if (IS_ERR(algt))
|
||||
return ERR_CAST(algt);
|
||||
|
||||
err = crypto_get_default_rng();
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
return PTR_ERR(algt);
|
||||
|
||||
if ((algt->type ^ CRYPTO_ALG_TYPE_AEAD) & CRYPTO_ALG_TYPE_MASK)
|
||||
inst = seqiv_ablkcipher_alloc(tb);
|
||||
err = seqiv_ablkcipher_create(tmpl, tb);
|
||||
else
|
||||
inst = seqiv_aead_alloc(tb);
|
||||
err = seqiv_aead_create(tmpl, tb);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int seqniv_create(struct crypto_template *tmpl, struct rtattr **tb)
|
||||
{
|
||||
struct aead_instance *inst;
|
||||
struct crypto_aead_spawn *spawn;
|
||||
struct aead_alg *alg;
|
||||
int err;
|
||||
|
||||
inst = aead_geniv_alloc(tmpl, tb, 0, 0);
|
||||
err = PTR_ERR(inst);
|
||||
if (IS_ERR(inst))
|
||||
goto put_rng;
|
||||
goto out;
|
||||
|
||||
inst->alg.cra_alignmask |= __alignof__(u32) - 1;
|
||||
inst->alg.cra_ctxsize += sizeof(struct seqiv_ctx);
|
||||
spawn = aead_instance_ctx(inst);
|
||||
alg = crypto_spawn_aead_alg(spawn);
|
||||
|
||||
if (alg->base.cra_aead.encrypt)
|
||||
goto done;
|
||||
|
||||
err = -EINVAL;
|
||||
if (inst->alg.ivsize != sizeof(u64))
|
||||
goto free_inst;
|
||||
|
||||
inst->alg.encrypt = seqniv_aead_encrypt;
|
||||
inst->alg.decrypt = seqniv_aead_decrypt;
|
||||
|
||||
inst->alg.base.cra_init = seqniv_aead_init;
|
||||
inst->alg.base.cra_exit = seqiv_aead_exit;
|
||||
|
||||
inst->alg.base.cra_alignmask |= __alignof__(u32) - 1;
|
||||
inst->alg.base.cra_ctxsize = sizeof(struct seqiv_aead_ctx);
|
||||
inst->alg.base.cra_ctxsize += inst->alg.ivsize;
|
||||
|
||||
done:
|
||||
err = aead_register_instance(tmpl, inst);
|
||||
if (err)
|
||||
goto free_inst;
|
||||
|
||||
out:
|
||||
return inst;
|
||||
return err;
|
||||
|
||||
put_rng:
|
||||
crypto_put_default_rng();
|
||||
free_inst:
|
||||
aead_geniv_free(inst);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -348,24 +752,46 @@ static void seqiv_free(struct crypto_instance *inst)
|
||||
if ((inst->alg.cra_flags ^ CRYPTO_ALG_TYPE_AEAD) & CRYPTO_ALG_TYPE_MASK)
|
||||
skcipher_geniv_free(inst);
|
||||
else
|
||||
aead_geniv_free(inst);
|
||||
crypto_put_default_rng();
|
||||
aead_geniv_free(aead_instance(inst));
|
||||
}
|
||||
|
||||
static struct crypto_template seqiv_tmpl = {
|
||||
.name = "seqiv",
|
||||
.alloc = seqiv_alloc,
|
||||
.create = seqiv_create,
|
||||
.free = seqiv_free,
|
||||
.module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct crypto_template seqniv_tmpl = {
|
||||
.name = "seqniv",
|
||||
.create = seqniv_create,
|
||||
.free = seqiv_free,
|
||||
.module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init seqiv_module_init(void)
|
||||
{
|
||||
return crypto_register_template(&seqiv_tmpl);
|
||||
int err;
|
||||
|
||||
err = crypto_register_template(&seqiv_tmpl);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = crypto_register_template(&seqniv_tmpl);
|
||||
if (err)
|
||||
goto out_undo_niv;
|
||||
|
||||
out:
|
||||
return err;
|
||||
|
||||
out_undo_niv:
|
||||
crypto_unregister_template(&seqiv_tmpl);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void __exit seqiv_module_exit(void)
|
||||
{
|
||||
crypto_unregister_template(&seqniv_tmpl);
|
||||
crypto_unregister_template(&seqiv_tmpl);
|
||||
}
|
||||
|
||||
@ -375,3 +801,4 @@ module_exit(seqiv_module_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Sequence Number IV Generator");
|
||||
MODULE_ALIAS_CRYPTO("seqiv");
|
||||
MODULE_ALIAS_CRYPTO("seqniv");
|
||||
|
@ -520,11 +520,6 @@ static int crypto_shash_init_tfm(struct crypto_tfm *tfm)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int crypto_shash_extsize(struct crypto_alg *alg)
|
||||
{
|
||||
return alg->cra_ctxsize;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET
|
||||
static int crypto_shash_report(struct sk_buff *skb, struct crypto_alg *alg)
|
||||
{
|
||||
@ -564,7 +559,7 @@ static void crypto_shash_show(struct seq_file *m, struct crypto_alg *alg)
|
||||
|
||||
static const struct crypto_type crypto_shash_type = {
|
||||
.ctxsize = crypto_shash_ctxsize,
|
||||
.extsize = crypto_shash_extsize,
|
||||
.extsize = crypto_alg_extsize,
|
||||
.init = crypto_init_shash_ops,
|
||||
.init_tfm = crypto_shash_init_tfm,
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
@ -22,8 +22,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <crypto/aead.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/fips.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/module.h>
|
||||
@ -34,7 +36,6 @@
|
||||
#include <linux/timex.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include "tcrypt.h"
|
||||
#include "internal.h"
|
||||
|
||||
/*
|
||||
* Need slab memory for testing (size in number of pages).
|
||||
@ -257,12 +258,12 @@ static void sg_init_aead(struct scatterlist *sg, char *xbuf[XBUFSIZE],
|
||||
rem = buflen % PAGE_SIZE;
|
||||
}
|
||||
|
||||
sg_init_table(sg, np);
|
||||
sg_init_table(sg, np + 1);
|
||||
np--;
|
||||
for (k = 0; k < np; k++)
|
||||
sg_set_buf(&sg[k], xbuf[k], PAGE_SIZE);
|
||||
sg_set_buf(&sg[k + 1], xbuf[k], PAGE_SIZE);
|
||||
|
||||
sg_set_buf(&sg[k], xbuf[k], rem);
|
||||
sg_set_buf(&sg[k + 1], xbuf[k], rem);
|
||||
}
|
||||
|
||||
static void test_aead_speed(const char *algo, int enc, unsigned int secs,
|
||||
@ -276,7 +277,6 @@ static void test_aead_speed(const char *algo, int enc, unsigned int secs,
|
||||
const char *key;
|
||||
struct aead_request *req;
|
||||
struct scatterlist *sg;
|
||||
struct scatterlist *asg;
|
||||
struct scatterlist *sgout;
|
||||
const char *e;
|
||||
void *assoc;
|
||||
@ -308,11 +308,10 @@ static void test_aead_speed(const char *algo, int enc, unsigned int secs,
|
||||
if (testmgr_alloc_buf(xoutbuf))
|
||||
goto out_nooutbuf;
|
||||
|
||||
sg = kmalloc(sizeof(*sg) * 8 * 3, GFP_KERNEL);
|
||||
sg = kmalloc(sizeof(*sg) * 9 * 2, GFP_KERNEL);
|
||||
if (!sg)
|
||||
goto out_nosg;
|
||||
asg = &sg[8];
|
||||
sgout = &asg[8];
|
||||
sgout = &sg[9];
|
||||
|
||||
tfm = crypto_alloc_aead(algo, 0, 0);
|
||||
|
||||
@ -338,7 +337,6 @@ static void test_aead_speed(const char *algo, int enc, unsigned int secs,
|
||||
do {
|
||||
assoc = axbuf[0];
|
||||
memset(assoc, 0xff, aad_size);
|
||||
sg_init_one(&asg[0], assoc, aad_size);
|
||||
|
||||
if ((*keysize + *b_size) > TVMEMSIZE * PAGE_SIZE) {
|
||||
pr_err("template (%u) too big for tvmem (%lu)\n",
|
||||
@ -374,14 +372,17 @@ static void test_aead_speed(const char *algo, int enc, unsigned int secs,
|
||||
goto out;
|
||||
}
|
||||
|
||||
sg_init_aead(&sg[0], xbuf,
|
||||
sg_init_aead(sg, xbuf,
|
||||
*b_size + (enc ? authsize : 0));
|
||||
|
||||
sg_init_aead(&sgout[0], xoutbuf,
|
||||
sg_init_aead(sgout, xoutbuf,
|
||||
*b_size + (enc ? authsize : 0));
|
||||
|
||||
sg_set_buf(&sg[0], assoc, aad_size);
|
||||
sg_set_buf(&sgout[0], assoc, aad_size);
|
||||
|
||||
aead_request_set_crypt(req, sg, sgout, *b_size, iv);
|
||||
aead_request_set_assoc(req, asg, aad_size);
|
||||
aead_request_set_ad(req, aad_size);
|
||||
|
||||
if (secs)
|
||||
ret = test_aead_jiffies(req, enc, *b_size,
|
||||
@ -808,7 +809,7 @@ static int test_ahash_jiffies(struct ahash_request *req, int blen,
|
||||
|
||||
for (start = jiffies, end = start + secs * HZ, bcount = 0;
|
||||
time_before(jiffies, end); bcount++) {
|
||||
ret = crypto_ahash_init(req);
|
||||
ret = do_one_ahash_op(req, crypto_ahash_init(req));
|
||||
if (ret)
|
||||
return ret;
|
||||
for (pcount = 0; pcount < blen; pcount += plen) {
|
||||
@ -877,7 +878,7 @@ static int test_ahash_cycles(struct ahash_request *req, int blen,
|
||||
|
||||
/* Warm-up run. */
|
||||
for (i = 0; i < 4; i++) {
|
||||
ret = crypto_ahash_init(req);
|
||||
ret = do_one_ahash_op(req, crypto_ahash_init(req));
|
||||
if (ret)
|
||||
goto out;
|
||||
for (pcount = 0; pcount < blen; pcount += plen) {
|
||||
@ -896,7 +897,7 @@ static int test_ahash_cycles(struct ahash_request *req, int blen,
|
||||
|
||||
start = get_cycles();
|
||||
|
||||
ret = crypto_ahash_init(req);
|
||||
ret = do_one_ahash_op(req, crypto_ahash_init(req));
|
||||
if (ret)
|
||||
goto out;
|
||||
for (pcount = 0; pcount < blen; pcount += plen) {
|
||||
@ -1761,6 +1762,11 @@ static int do_test(const char *alg, u32 type, u32 mask, int m)
|
||||
NULL, 0, 16, 8, aead_speed_template_20);
|
||||
break;
|
||||
|
||||
case 212:
|
||||
test_aead_speed("rfc4309(ccm(aes))", ENCRYPT, sec,
|
||||
NULL, 0, 16, 8, aead_speed_template_19);
|
||||
break;
|
||||
|
||||
case 300:
|
||||
if (alg) {
|
||||
test_hash_speed(alg, sec, generic_hash_speed_template);
|
||||
|
@ -65,6 +65,7 @@ static u8 speed_template_32_64[] = {32, 64, 0};
|
||||
/*
|
||||
* AEAD speed tests
|
||||
*/
|
||||
static u8 aead_speed_template_19[] = {19, 0};
|
||||
static u8 aead_speed_template_20[] = {20, 0};
|
||||
|
||||
/*
|
||||
|
314
crypto/testmgr.c
314
crypto/testmgr.c
@ -20,14 +20,17 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <crypto/aead.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/fips.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <crypto/rng.h>
|
||||
#include <crypto/drbg.h>
|
||||
#include <crypto/akcipher.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
@ -114,6 +117,11 @@ struct drbg_test_suite {
|
||||
unsigned int count;
|
||||
};
|
||||
|
||||
struct akcipher_test_suite {
|
||||
struct akcipher_testvec *vecs;
|
||||
unsigned int count;
|
||||
};
|
||||
|
||||
struct alg_test_desc {
|
||||
const char *alg;
|
||||
int (*test)(const struct alg_test_desc *desc, const char *driver,
|
||||
@ -128,6 +136,7 @@ struct alg_test_desc {
|
||||
struct hash_test_suite hash;
|
||||
struct cprng_test_suite cprng;
|
||||
struct drbg_test_suite drbg;
|
||||
struct akcipher_test_suite akcipher;
|
||||
} suite;
|
||||
};
|
||||
|
||||
@ -425,7 +434,6 @@ static int __test_aead(struct crypto_aead *tfm, int enc,
|
||||
char *key;
|
||||
struct aead_request *req;
|
||||
struct scatterlist *sg;
|
||||
struct scatterlist *asg;
|
||||
struct scatterlist *sgout;
|
||||
const char *e, *d;
|
||||
struct tcrypt_result result;
|
||||
@ -452,11 +460,10 @@ static int __test_aead(struct crypto_aead *tfm, int enc,
|
||||
goto out_nooutbuf;
|
||||
|
||||
/* avoid "the frame size is larger than 1024 bytes" compiler warning */
|
||||
sg = kmalloc(sizeof(*sg) * 8 * (diff_dst ? 3 : 2), GFP_KERNEL);
|
||||
sg = kmalloc(sizeof(*sg) * 8 * (diff_dst ? 4 : 2), GFP_KERNEL);
|
||||
if (!sg)
|
||||
goto out_nosg;
|
||||
asg = &sg[8];
|
||||
sgout = &asg[8];
|
||||
sgout = &sg[16];
|
||||
|
||||
if (diff_dst)
|
||||
d = "-ddst";
|
||||
@ -535,23 +542,27 @@ static int __test_aead(struct crypto_aead *tfm, int enc,
|
||||
goto out;
|
||||
}
|
||||
|
||||
k = !!template[i].alen;
|
||||
sg_init_table(sg, k + 1);
|
||||
sg_set_buf(&sg[0], assoc, template[i].alen);
|
||||
sg_set_buf(&sg[k], input,
|
||||
template[i].ilen + (enc ? authsize : 0));
|
||||
output = input;
|
||||
|
||||
if (diff_dst) {
|
||||
sg_init_table(sgout, k + 1);
|
||||
sg_set_buf(&sgout[0], assoc, template[i].alen);
|
||||
|
||||
output = xoutbuf[0];
|
||||
output += align_offset;
|
||||
sg_init_one(&sg[0], input, template[i].ilen);
|
||||
sg_init_one(&sgout[0], output, template[i].rlen);
|
||||
} else {
|
||||
sg_init_one(&sg[0], input,
|
||||
template[i].ilen + (enc ? authsize : 0));
|
||||
output = input;
|
||||
sg_set_buf(&sgout[k], output,
|
||||
template[i].rlen + (enc ? 0 : authsize));
|
||||
}
|
||||
|
||||
sg_init_one(&asg[0], assoc, template[i].alen);
|
||||
|
||||
aead_request_set_crypt(req, sg, (diff_dst) ? sgout : sg,
|
||||
template[i].ilen, iv);
|
||||
|
||||
aead_request_set_assoc(req, asg, template[i].alen);
|
||||
aead_request_set_ad(req, template[i].alen);
|
||||
|
||||
ret = enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req);
|
||||
|
||||
@ -631,9 +642,29 @@ static int __test_aead(struct crypto_aead *tfm, int enc,
|
||||
authsize = abs(template[i].rlen - template[i].ilen);
|
||||
|
||||
ret = -EINVAL;
|
||||
sg_init_table(sg, template[i].np);
|
||||
sg_init_table(sg, template[i].anp + template[i].np);
|
||||
if (diff_dst)
|
||||
sg_init_table(sgout, template[i].np);
|
||||
sg_init_table(sgout, template[i].anp + template[i].np);
|
||||
|
||||
ret = -EINVAL;
|
||||
for (k = 0, temp = 0; k < template[i].anp; k++) {
|
||||
if (WARN_ON(offset_in_page(IDX[k]) +
|
||||
template[i].atap[k] > PAGE_SIZE))
|
||||
goto out;
|
||||
sg_set_buf(&sg[k],
|
||||
memcpy(axbuf[IDX[k] >> PAGE_SHIFT] +
|
||||
offset_in_page(IDX[k]),
|
||||
template[i].assoc + temp,
|
||||
template[i].atap[k]),
|
||||
template[i].atap[k]);
|
||||
if (diff_dst)
|
||||
sg_set_buf(&sgout[k],
|
||||
axbuf[IDX[k] >> PAGE_SHIFT] +
|
||||
offset_in_page(IDX[k]),
|
||||
template[i].atap[k]);
|
||||
temp += template[i].atap[k];
|
||||
}
|
||||
|
||||
for (k = 0, temp = 0; k < template[i].np; k++) {
|
||||
if (WARN_ON(offset_in_page(IDX[k]) +
|
||||
template[i].tap[k] > PAGE_SIZE))
|
||||
@ -641,7 +672,8 @@ static int __test_aead(struct crypto_aead *tfm, int enc,
|
||||
|
||||
q = xbuf[IDX[k] >> PAGE_SHIFT] + offset_in_page(IDX[k]);
|
||||
memcpy(q, template[i].input + temp, template[i].tap[k]);
|
||||
sg_set_buf(&sg[k], q, template[i].tap[k]);
|
||||
sg_set_buf(&sg[template[i].anp + k],
|
||||
q, template[i].tap[k]);
|
||||
|
||||
if (diff_dst) {
|
||||
q = xoutbuf[IDX[k] >> PAGE_SHIFT] +
|
||||
@ -649,7 +681,8 @@ static int __test_aead(struct crypto_aead *tfm, int enc,
|
||||
|
||||
memset(q, 0, template[i].tap[k]);
|
||||
|
||||
sg_set_buf(&sgout[k], q, template[i].tap[k]);
|
||||
sg_set_buf(&sgout[template[i].anp + k],
|
||||
q, template[i].tap[k]);
|
||||
}
|
||||
|
||||
n = template[i].tap[k];
|
||||
@ -669,39 +702,24 @@ static int __test_aead(struct crypto_aead *tfm, int enc,
|
||||
}
|
||||
|
||||
if (enc) {
|
||||
if (WARN_ON(sg[k - 1].offset +
|
||||
sg[k - 1].length + authsize >
|
||||
PAGE_SIZE)) {
|
||||
if (WARN_ON(sg[template[i].anp + k - 1].offset +
|
||||
sg[template[i].anp + k - 1].length +
|
||||
authsize > PAGE_SIZE)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (diff_dst)
|
||||
sgout[k - 1].length += authsize;
|
||||
else
|
||||
sg[k - 1].length += authsize;
|
||||
}
|
||||
|
||||
sg_init_table(asg, template[i].anp);
|
||||
ret = -EINVAL;
|
||||
for (k = 0, temp = 0; k < template[i].anp; k++) {
|
||||
if (WARN_ON(offset_in_page(IDX[k]) +
|
||||
template[i].atap[k] > PAGE_SIZE))
|
||||
goto out;
|
||||
sg_set_buf(&asg[k],
|
||||
memcpy(axbuf[IDX[k] >> PAGE_SHIFT] +
|
||||
offset_in_page(IDX[k]),
|
||||
template[i].assoc + temp,
|
||||
template[i].atap[k]),
|
||||
template[i].atap[k]);
|
||||
temp += template[i].atap[k];
|
||||
sgout[template[i].anp + k - 1].length +=
|
||||
authsize;
|
||||
sg[template[i].anp + k - 1].length += authsize;
|
||||
}
|
||||
|
||||
aead_request_set_crypt(req, sg, (diff_dst) ? sgout : sg,
|
||||
template[i].ilen,
|
||||
iv);
|
||||
|
||||
aead_request_set_assoc(req, asg, template[i].alen);
|
||||
aead_request_set_ad(req, template[i].alen);
|
||||
|
||||
ret = enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req);
|
||||
|
||||
@ -1814,6 +1832,147 @@ static int alg_test_drbg(const struct alg_test_desc *desc, const char *driver,
|
||||
|
||||
}
|
||||
|
||||
static int do_test_rsa(struct crypto_akcipher *tfm,
|
||||
struct akcipher_testvec *vecs)
|
||||
{
|
||||
struct akcipher_request *req;
|
||||
void *outbuf_enc = NULL;
|
||||
void *outbuf_dec = NULL;
|
||||
struct tcrypt_result result;
|
||||
unsigned int out_len_max, out_len = 0;
|
||||
int err = -ENOMEM;
|
||||
|
||||
req = akcipher_request_alloc(tfm, GFP_KERNEL);
|
||||
if (!req)
|
||||
return err;
|
||||
|
||||
init_completion(&result.completion);
|
||||
err = crypto_akcipher_setkey(tfm, vecs->key, vecs->key_len);
|
||||
if (err)
|
||||
goto free_req;
|
||||
|
||||
akcipher_request_set_crypt(req, vecs->m, outbuf_enc, vecs->m_size,
|
||||
out_len);
|
||||
/* expect this to fail, and update the required buf len */
|
||||
crypto_akcipher_encrypt(req);
|
||||
out_len = req->dst_len;
|
||||
if (!out_len) {
|
||||
err = -EINVAL;
|
||||
goto free_req;
|
||||
}
|
||||
|
||||
out_len_max = out_len;
|
||||
err = -ENOMEM;
|
||||
outbuf_enc = kzalloc(out_len_max, GFP_KERNEL);
|
||||
if (!outbuf_enc)
|
||||
goto free_req;
|
||||
|
||||
akcipher_request_set_crypt(req, vecs->m, outbuf_enc, vecs->m_size,
|
||||
out_len);
|
||||
akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
|
||||
tcrypt_complete, &result);
|
||||
|
||||
/* Run RSA encrypt - c = m^e mod n;*/
|
||||
err = wait_async_op(&result, crypto_akcipher_encrypt(req));
|
||||
if (err) {
|
||||
pr_err("alg: rsa: encrypt test failed. err %d\n", err);
|
||||
goto free_all;
|
||||
}
|
||||
if (out_len != vecs->c_size) {
|
||||
pr_err("alg: rsa: encrypt test failed. Invalid output len\n");
|
||||
err = -EINVAL;
|
||||
goto free_all;
|
||||
}
|
||||
/* verify that encrypted message is equal to expected */
|
||||
if (memcmp(vecs->c, outbuf_enc, vecs->c_size)) {
|
||||
pr_err("alg: rsa: encrypt test failed. Invalid output\n");
|
||||
err = -EINVAL;
|
||||
goto free_all;
|
||||
}
|
||||
/* Don't invoke decrypt for vectors with public key */
|
||||
if (vecs->public_key_vec) {
|
||||
err = 0;
|
||||
goto free_all;
|
||||
}
|
||||
outbuf_dec = kzalloc(out_len_max, GFP_KERNEL);
|
||||
if (!outbuf_dec) {
|
||||
err = -ENOMEM;
|
||||
goto free_all;
|
||||
}
|
||||
init_completion(&result.completion);
|
||||
akcipher_request_set_crypt(req, outbuf_enc, outbuf_dec, vecs->c_size,
|
||||
out_len);
|
||||
|
||||
/* Run RSA decrypt - m = c^d mod n;*/
|
||||
err = wait_async_op(&result, crypto_akcipher_decrypt(req));
|
||||
if (err) {
|
||||
pr_err("alg: rsa: decrypt test failed. err %d\n", err);
|
||||
goto free_all;
|
||||
}
|
||||
out_len = req->dst_len;
|
||||
if (out_len != vecs->m_size) {
|
||||
pr_err("alg: rsa: decrypt test failed. Invalid output len\n");
|
||||
err = -EINVAL;
|
||||
goto free_all;
|
||||
}
|
||||
/* verify that decrypted message is equal to the original msg */
|
||||
if (memcmp(vecs->m, outbuf_dec, vecs->m_size)) {
|
||||
pr_err("alg: rsa: decrypt test failed. Invalid output\n");
|
||||
err = -EINVAL;
|
||||
}
|
||||
free_all:
|
||||
kfree(outbuf_dec);
|
||||
kfree(outbuf_enc);
|
||||
free_req:
|
||||
akcipher_request_free(req);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int test_rsa(struct crypto_akcipher *tfm, struct akcipher_testvec *vecs,
|
||||
unsigned int tcount)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < tcount; i++) {
|
||||
ret = do_test_rsa(tfm, vecs++);
|
||||
if (ret) {
|
||||
pr_err("alg: rsa: test failed on vector %d, err=%d\n",
|
||||
i + 1, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_akcipher(struct crypto_akcipher *tfm, const char *alg,
|
||||
struct akcipher_testvec *vecs, unsigned int tcount)
|
||||
{
|
||||
if (strncmp(alg, "rsa", 3) == 0)
|
||||
return test_rsa(tfm, vecs, tcount);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alg_test_akcipher(const struct alg_test_desc *desc,
|
||||
const char *driver, u32 type, u32 mask)
|
||||
{
|
||||
struct crypto_akcipher *tfm;
|
||||
int err = 0;
|
||||
|
||||
tfm = crypto_alloc_akcipher(driver, type | CRYPTO_ALG_INTERNAL, mask);
|
||||
if (IS_ERR(tfm)) {
|
||||
pr_err("alg: akcipher: Failed to load tfm for %s: %ld\n",
|
||||
driver, PTR_ERR(tfm));
|
||||
return PTR_ERR(tfm);
|
||||
}
|
||||
if (desc->suite.akcipher.vecs)
|
||||
err = test_akcipher(tfm, desc->alg, desc->suite.akcipher.vecs,
|
||||
desc->suite.akcipher.count);
|
||||
|
||||
crypto_free_akcipher(tfm);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int alg_test_null(const struct alg_test_desc *desc,
|
||||
const char *driver, u32 type, u32 mask)
|
||||
{
|
||||
@ -2296,6 +2455,21 @@ static const struct alg_test_desc alg_test_descs[] = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
.alg = "chacha20",
|
||||
.test = alg_test_skcipher,
|
||||
.suite = {
|
||||
.cipher = {
|
||||
.enc = {
|
||||
.vecs = chacha20_enc_tv_template,
|
||||
.count = CHACHA20_ENC_TEST_VECTORS
|
||||
},
|
||||
.dec = {
|
||||
.vecs = chacha20_enc_tv_template,
|
||||
.count = CHACHA20_ENC_TEST_VECTORS
|
||||
},
|
||||
}
|
||||
}
|
||||
}, {
|
||||
.alg = "cmac(aes)",
|
||||
.test = alg_test_hash,
|
||||
@ -2317,6 +2491,15 @@ static const struct alg_test_desc alg_test_descs[] = {
|
||||
}, {
|
||||
.alg = "compress_null",
|
||||
.test = alg_test_null,
|
||||
}, {
|
||||
.alg = "crc32",
|
||||
.test = alg_test_hash,
|
||||
.suite = {
|
||||
.hash = {
|
||||
.vecs = crc32_tv_template,
|
||||
.count = CRC32_TEST_VECTORS
|
||||
}
|
||||
}
|
||||
}, {
|
||||
.alg = "crc32c",
|
||||
.test = alg_test_crc32c,
|
||||
@ -3094,6 +3277,10 @@ static const struct alg_test_desc alg_test_descs[] = {
|
||||
.count = HMAC_SHA512_TEST_VECTORS
|
||||
}
|
||||
}
|
||||
}, {
|
||||
.alg = "jitterentropy_rng",
|
||||
.fips_allowed = 1,
|
||||
.test = alg_test_null,
|
||||
}, {
|
||||
.alg = "lrw(aes)",
|
||||
.test = alg_test_skcipher,
|
||||
@ -3275,6 +3462,15 @@ static const struct alg_test_desc alg_test_descs[] = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
.alg = "poly1305",
|
||||
.test = alg_test_hash,
|
||||
.suite = {
|
||||
.hash = {
|
||||
.vecs = poly1305_tv_template,
|
||||
.count = POLY1305_TEST_VECTORS
|
||||
}
|
||||
}
|
||||
}, {
|
||||
.alg = "rfc3686(ctr(aes))",
|
||||
.test = alg_test_skcipher,
|
||||
@ -3338,6 +3534,36 @@ static const struct alg_test_desc alg_test_descs[] = {
|
||||
},
|
||||
}
|
||||
}
|
||||
}, {
|
||||
.alg = "rfc7539(chacha20,poly1305)",
|
||||
.test = alg_test_aead,
|
||||
.suite = {
|
||||
.aead = {
|
||||
.enc = {
|
||||
.vecs = rfc7539_enc_tv_template,
|
||||
.count = RFC7539_ENC_TEST_VECTORS
|
||||
},
|
||||
.dec = {
|
||||
.vecs = rfc7539_dec_tv_template,
|
||||
.count = RFC7539_DEC_TEST_VECTORS
|
||||
},
|
||||
}
|
||||
}
|
||||
}, {
|
||||
.alg = "rfc7539esp(chacha20,poly1305)",
|
||||
.test = alg_test_aead,
|
||||
.suite = {
|
||||
.aead = {
|
||||
.enc = {
|
||||
.vecs = rfc7539esp_enc_tv_template,
|
||||
.count = RFC7539ESP_ENC_TEST_VECTORS
|
||||
},
|
||||
.dec = {
|
||||
.vecs = rfc7539esp_dec_tv_template,
|
||||
.count = RFC7539ESP_DEC_TEST_VECTORS
|
||||
},
|
||||
}
|
||||
}
|
||||
}, {
|
||||
.alg = "rmd128",
|
||||
.test = alg_test_hash,
|
||||
@ -3374,6 +3600,16 @@ static const struct alg_test_desc alg_test_descs[] = {
|
||||
.count = RMD320_TEST_VECTORS
|
||||
}
|
||||
}
|
||||
}, {
|
||||
.alg = "rsa",
|
||||
.test = alg_test_akcipher,
|
||||
.fips_allowed = 1,
|
||||
.suite = {
|
||||
.akcipher = {
|
||||
.vecs = rsa_tv_template,
|
||||
.count = RSA_TEST_VECTORS
|
||||
}
|
||||
}
|
||||
}, {
|
||||
.alg = "salsa20",
|
||||
.test = alg_test_skcipher,
|
||||
|
2443
crypto/testmgr.h
2443
crypto/testmgr.h
File diff suppressed because it is too large
Load Diff
@ -78,7 +78,7 @@ static void zlib_exit(struct crypto_tfm *tfm)
|
||||
}
|
||||
|
||||
|
||||
static int zlib_compress_setup(struct crypto_pcomp *tfm, void *params,
|
||||
static int zlib_compress_setup(struct crypto_pcomp *tfm, const void *params,
|
||||
unsigned int len)
|
||||
{
|
||||
struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
|
||||
@ -209,7 +209,7 @@ static int zlib_compress_final(struct crypto_pcomp *tfm,
|
||||
}
|
||||
|
||||
|
||||
static int zlib_decompress_setup(struct crypto_pcomp *tfm, void *params,
|
||||
static int zlib_decompress_setup(struct crypto_pcomp *tfm, const void *params,
|
||||
unsigned int len)
|
||||
{
|
||||
struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
|
||||
|
@ -57,6 +57,7 @@
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
|
||||
/*
|
||||
@ -152,13 +153,39 @@ struct mvebu_mbus_state {
|
||||
|
||||
static struct mvebu_mbus_state mbus_state;
|
||||
|
||||
/*
|
||||
* We provide two variants of the mv_mbus_dram_info() function:
|
||||
*
|
||||
* - The normal one, where the described DRAM ranges may overlap with
|
||||
* the I/O windows, but for which the DRAM ranges are guaranteed to
|
||||
* have a power of two size. Such ranges are suitable for the DMA
|
||||
* masters that only DMA between the RAM and the device, which is
|
||||
* actually all devices except the crypto engines.
|
||||
*
|
||||
* - The 'nooverlap' one, where the described DRAM ranges are
|
||||
* guaranteed to not overlap with the I/O windows, but for which the
|
||||
* DRAM ranges will not have power of two sizes. They will only be
|
||||
* aligned on a 64 KB boundary, and have a size multiple of 64
|
||||
* KB. Such ranges are suitable for the DMA masters that DMA between
|
||||
* the crypto SRAM (which is mapped through an I/O window) and a
|
||||
* device. This is the case for the crypto engines.
|
||||
*/
|
||||
|
||||
static struct mbus_dram_target_info mvebu_mbus_dram_info;
|
||||
static struct mbus_dram_target_info mvebu_mbus_dram_info_nooverlap;
|
||||
|
||||
const struct mbus_dram_target_info *mv_mbus_dram_info(void)
|
||||
{
|
||||
return &mvebu_mbus_dram_info;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mv_mbus_dram_info);
|
||||
|
||||
const struct mbus_dram_target_info *mv_mbus_dram_info_nooverlap(void)
|
||||
{
|
||||
return &mvebu_mbus_dram_info_nooverlap;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mv_mbus_dram_info_nooverlap);
|
||||
|
||||
/* Checks whether the given window has remap capability */
|
||||
static bool mvebu_mbus_window_is_remappable(struct mvebu_mbus_state *mbus,
|
||||
const int win)
|
||||
@ -576,6 +603,95 @@ static unsigned int armada_xp_mbus_win_remap_offset(int win)
|
||||
return MVEBU_MBUS_NO_REMAP;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use the memblock information to find the MBus bridge hole in the
|
||||
* physical address space.
|
||||
*/
|
||||
static void __init
|
||||
mvebu_mbus_find_bridge_hole(uint64_t *start, uint64_t *end)
|
||||
{
|
||||
struct memblock_region *r;
|
||||
uint64_t s = 0;
|
||||
|
||||
for_each_memblock(memory, r) {
|
||||
/*
|
||||
* This part of the memory is above 4 GB, so we don't
|
||||
* care for the MBus bridge hole.
|
||||
*/
|
||||
if (r->base >= 0x100000000ULL)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* The MBus bridge hole is at the end of the RAM under
|
||||
* the 4 GB limit.
|
||||
*/
|
||||
if (r->base + r->size > s)
|
||||
s = r->base + r->size;
|
||||
}
|
||||
|
||||
*start = s;
|
||||
*end = 0x100000000ULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function fills in the mvebu_mbus_dram_info_nooverlap data
|
||||
* structure, by looking at the mvebu_mbus_dram_info data, and
|
||||
* removing the parts of it that overlap with I/O windows.
|
||||
*/
|
||||
static void __init
|
||||
mvebu_mbus_setup_cpu_target_nooverlap(struct mvebu_mbus_state *mbus)
|
||||
{
|
||||
uint64_t mbus_bridge_base, mbus_bridge_end;
|
||||
int cs_nooverlap = 0;
|
||||
int i;
|
||||
|
||||
mvebu_mbus_find_bridge_hole(&mbus_bridge_base, &mbus_bridge_end);
|
||||
|
||||
for (i = 0; i < mvebu_mbus_dram_info.num_cs; i++) {
|
||||
struct mbus_dram_window *w;
|
||||
u64 base, size, end;
|
||||
|
||||
w = &mvebu_mbus_dram_info.cs[i];
|
||||
base = w->base;
|
||||
size = w->size;
|
||||
end = base + size;
|
||||
|
||||
/*
|
||||
* The CS is fully enclosed inside the MBus bridge
|
||||
* area, so ignore it.
|
||||
*/
|
||||
if (base >= mbus_bridge_base && end <= mbus_bridge_end)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Beginning of CS overlaps with end of MBus, raise CS
|
||||
* base address, and shrink its size.
|
||||
*/
|
||||
if (base >= mbus_bridge_base && end > mbus_bridge_end) {
|
||||
size -= mbus_bridge_end - base;
|
||||
base = mbus_bridge_end;
|
||||
}
|
||||
|
||||
/*
|
||||
* End of CS overlaps with beginning of MBus, shrink
|
||||
* CS size.
|
||||
*/
|
||||
if (base < mbus_bridge_base && end > mbus_bridge_base)
|
||||
size -= end - mbus_bridge_base;
|
||||
|
||||
w = &mvebu_mbus_dram_info_nooverlap.cs[cs_nooverlap++];
|
||||
w->cs_index = i;
|
||||
w->mbus_attr = 0xf & ~(1 << i);
|
||||
if (mbus->hw_io_coherency)
|
||||
w->mbus_attr |= ATTR_HW_COHERENCY;
|
||||
w->base = base;
|
||||
w->size = size;
|
||||
}
|
||||
|
||||
mvebu_mbus_dram_info_nooverlap.mbus_dram_target_id = TARGET_DDR;
|
||||
mvebu_mbus_dram_info_nooverlap.num_cs = cs_nooverlap;
|
||||
}
|
||||
|
||||
static void __init
|
||||
mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus)
|
||||
{
|
||||
@ -964,6 +1080,7 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
|
||||
mvebu_mbus_disable_window(mbus, win);
|
||||
|
||||
mbus->soc->setup_cpu_target(mbus);
|
||||
mvebu_mbus_setup_cpu_target_nooverlap(mbus);
|
||||
|
||||
if (is_coherent)
|
||||
writel(UNIT_SYNC_BARRIER_ALL,
|
||||
|
@ -409,6 +409,9 @@ static DECLARE_WAIT_QUEUE_HEAD(random_write_wait);
|
||||
static DECLARE_WAIT_QUEUE_HEAD(urandom_init_wait);
|
||||
static struct fasync_struct *fasync;
|
||||
|
||||
static DEFINE_SPINLOCK(random_ready_list_lock);
|
||||
static LIST_HEAD(random_ready_list);
|
||||
|
||||
/**********************************************************************
|
||||
*
|
||||
* OS independent entropy store. Here are the functions which handle
|
||||
@ -589,6 +592,22 @@ static void fast_mix(struct fast_pool *f)
|
||||
f->count++;
|
||||
}
|
||||
|
||||
static void process_random_ready_list(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct random_ready_callback *rdy, *tmp;
|
||||
|
||||
spin_lock_irqsave(&random_ready_list_lock, flags);
|
||||
list_for_each_entry_safe(rdy, tmp, &random_ready_list, list) {
|
||||
struct module *owner = rdy->owner;
|
||||
|
||||
list_del_init(&rdy->list);
|
||||
rdy->func(rdy);
|
||||
module_put(owner);
|
||||
}
|
||||
spin_unlock_irqrestore(&random_ready_list_lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Credit (or debit) the entropy store with n bits of entropy.
|
||||
* Use credit_entropy_bits_safe() if the value comes from userspace
|
||||
@ -660,7 +679,8 @@ retry:
|
||||
r->entropy_total = 0;
|
||||
if (r == &nonblocking_pool) {
|
||||
prandom_reseed_late();
|
||||
wake_up_interruptible(&urandom_init_wait);
|
||||
process_random_ready_list();
|
||||
wake_up_all(&urandom_init_wait);
|
||||
pr_notice("random: %s pool is initialized\n", r->name);
|
||||
}
|
||||
}
|
||||
@ -1244,6 +1264,64 @@ void get_random_bytes(void *buf, int nbytes)
|
||||
}
|
||||
EXPORT_SYMBOL(get_random_bytes);
|
||||
|
||||
/*
|
||||
* Add a callback function that will be invoked when the nonblocking
|
||||
* pool is initialised.
|
||||
*
|
||||
* returns: 0 if callback is successfully added
|
||||
* -EALREADY if pool is already initialised (callback not called)
|
||||
* -ENOENT if module for callback is not alive
|
||||
*/
|
||||
int add_random_ready_callback(struct random_ready_callback *rdy)
|
||||
{
|
||||
struct module *owner;
|
||||
unsigned long flags;
|
||||
int err = -EALREADY;
|
||||
|
||||
if (likely(nonblocking_pool.initialized))
|
||||
return err;
|
||||
|
||||
owner = rdy->owner;
|
||||
if (!try_module_get(owner))
|
||||
return -ENOENT;
|
||||
|
||||
spin_lock_irqsave(&random_ready_list_lock, flags);
|
||||
if (nonblocking_pool.initialized)
|
||||
goto out;
|
||||
|
||||
owner = NULL;
|
||||
|
||||
list_add(&rdy->list, &random_ready_list);
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&random_ready_list_lock, flags);
|
||||
|
||||
module_put(owner);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(add_random_ready_callback);
|
||||
|
||||
/*
|
||||
* Delete a previously registered readiness callback function.
|
||||
*/
|
||||
void del_random_ready_callback(struct random_ready_callback *rdy)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct module *owner = NULL;
|
||||
|
||||
spin_lock_irqsave(&random_ready_list_lock, flags);
|
||||
if (!list_empty(&rdy->list)) {
|
||||
list_del_init(&rdy->list);
|
||||
owner = rdy->owner;
|
||||
}
|
||||
spin_unlock_irqrestore(&random_ready_list_lock, flags);
|
||||
|
||||
module_put(owner);
|
||||
}
|
||||
EXPORT_SYMBOL(del_random_ready_callback);
|
||||
|
||||
/*
|
||||
* This function will use the architecture-specific hardware random
|
||||
* number generator if it is available. The arch-specific hw RNG will
|
||||
|
@ -162,10 +162,10 @@ config CRYPTO_GHASH_S390
|
||||
config CRYPTO_DEV_MV_CESA
|
||||
tristate "Marvell's Cryptographic Engine"
|
||||
depends on PLAT_ORION
|
||||
select CRYPTO_ALGAPI
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_BLKCIPHER2
|
||||
select CRYPTO_BLKCIPHER
|
||||
select CRYPTO_HASH
|
||||
select SRAM
|
||||
help
|
||||
This driver allows you to utilize the Cryptographic Engines and
|
||||
Security Accelerator (CESA) which can be found on the Marvell Orion
|
||||
@ -173,10 +173,27 @@ config CRYPTO_DEV_MV_CESA
|
||||
|
||||
Currently the driver supports AES in ECB and CBC mode without DMA.
|
||||
|
||||
config CRYPTO_DEV_MARVELL_CESA
|
||||
tristate "New Marvell's Cryptographic Engine driver"
|
||||
depends on PLAT_ORION || ARCH_MVEBU
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_DES
|
||||
select CRYPTO_BLKCIPHER
|
||||
select CRYPTO_HASH
|
||||
select SRAM
|
||||
help
|
||||
This driver allows you to utilize the Cryptographic Engines and
|
||||
Security Accelerator (CESA) which can be found on the Armada 370.
|
||||
This driver supports CPU offload through DMA transfers.
|
||||
|
||||
This driver is aimed at replacing the mv_cesa driver. This will only
|
||||
happen once it has received proper testing.
|
||||
|
||||
config CRYPTO_DEV_NIAGARA2
|
||||
tristate "Niagara2 Stream Processing Unit driver"
|
||||
select CRYPTO_DES
|
||||
select CRYPTO_ALGAPI
|
||||
select CRYPTO_BLKCIPHER
|
||||
select CRYPTO_HASH
|
||||
depends on SPARC64
|
||||
help
|
||||
Each core of a Niagara2 processor contains a Stream
|
||||
@ -189,7 +206,6 @@ config CRYPTO_DEV_NIAGARA2
|
||||
config CRYPTO_DEV_HIFN_795X
|
||||
tristate "Driver HIFN 795x crypto accelerator chips"
|
||||
select CRYPTO_DES
|
||||
select CRYPTO_ALGAPI
|
||||
select CRYPTO_BLKCIPHER
|
||||
select HW_RANDOM if CRYPTO_DEV_HIFN_795X_RNG
|
||||
depends on PCI
|
||||
@ -208,8 +224,10 @@ source drivers/crypto/caam/Kconfig
|
||||
|
||||
config CRYPTO_DEV_TALITOS
|
||||
tristate "Talitos Freescale Security Engine (SEC)"
|
||||
select CRYPTO_ALGAPI
|
||||
select CRYPTO_AEAD
|
||||
select CRYPTO_AUTHENC
|
||||
select CRYPTO_BLKCIPHER
|
||||
select CRYPTO_HASH
|
||||
select HW_RANDOM
|
||||
depends on FSL_SOC
|
||||
help
|
||||
@ -222,11 +240,29 @@ config CRYPTO_DEV_TALITOS
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called talitos.
|
||||
|
||||
config CRYPTO_DEV_TALITOS1
|
||||
bool "SEC1 (SEC 1.0 and SEC Lite 1.2)"
|
||||
depends on CRYPTO_DEV_TALITOS
|
||||
depends on PPC_8xx || PPC_82xx
|
||||
default y
|
||||
help
|
||||
Say 'Y' here to use the Freescale Security Engine (SEC) version 1.0
|
||||
found on MPC82xx or the Freescale Security Engine (SEC Lite)
|
||||
version 1.2 found on MPC8xx
|
||||
|
||||
config CRYPTO_DEV_TALITOS2
|
||||
bool "SEC2+ (SEC version 2.0 or upper)"
|
||||
depends on CRYPTO_DEV_TALITOS
|
||||
default y if !PPC_8xx
|
||||
help
|
||||
Say 'Y' here to use the Freescale Security Engine (SEC)
|
||||
version 2 and following as found on MPC83xx, MPC85xx, etc ...
|
||||
|
||||
config CRYPTO_DEV_IXP4XX
|
||||
tristate "Driver for IXP4xx crypto hardware acceleration"
|
||||
depends on ARCH_IXP4XX && IXP4XX_QMGR && IXP4XX_NPE
|
||||
select CRYPTO_DES
|
||||
select CRYPTO_ALGAPI
|
||||
select CRYPTO_AEAD
|
||||
select CRYPTO_AUTHENC
|
||||
select CRYPTO_BLKCIPHER
|
||||
help
|
||||
@ -236,7 +272,6 @@ config CRYPTO_DEV_PPC4XX
|
||||
tristate "Driver AMCC PPC4xx crypto accelerator"
|
||||
depends on PPC && 4xx
|
||||
select CRYPTO_HASH
|
||||
select CRYPTO_ALGAPI
|
||||
select CRYPTO_BLKCIPHER
|
||||
help
|
||||
This option allows you to have support for AMCC crypto acceleration.
|
||||
@ -257,7 +292,7 @@ config CRYPTO_DEV_OMAP_AES
|
||||
tristate "Support for OMAP AES hw engine"
|
||||
depends on ARCH_OMAP2 || ARCH_OMAP3 || ARCH_OMAP2PLUS
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_BLKCIPHER2
|
||||
select CRYPTO_BLKCIPHER
|
||||
help
|
||||
OMAP processors have AES module accelerator. Select this if you
|
||||
want to use the OMAP module for AES algorithms.
|
||||
@ -266,7 +301,7 @@ config CRYPTO_DEV_OMAP_DES
|
||||
tristate "Support for OMAP DES3DES hw engine"
|
||||
depends on ARCH_OMAP2PLUS
|
||||
select CRYPTO_DES
|
||||
select CRYPTO_BLKCIPHER2
|
||||
select CRYPTO_BLKCIPHER
|
||||
help
|
||||
OMAP processors have DES/3DES module accelerator. Select this if you
|
||||
want to use the OMAP module for DES and 3DES algorithms. Currently
|
||||
@ -276,9 +311,10 @@ config CRYPTO_DEV_OMAP_DES
|
||||
config CRYPTO_DEV_PICOXCELL
|
||||
tristate "Support for picoXcell IPSEC and Layer2 crypto engines"
|
||||
depends on ARCH_PICOXCELL && HAVE_CLK
|
||||
select CRYPTO_AEAD
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_AUTHENC
|
||||
select CRYPTO_ALGAPI
|
||||
select CRYPTO_BLKCIPHER
|
||||
select CRYPTO_DES
|
||||
select CRYPTO_CBC
|
||||
select CRYPTO_ECB
|
||||
@ -304,7 +340,6 @@ config CRYPTO_DEV_S5P
|
||||
tristate "Support for Samsung S5PV210/Exynos crypto accelerator"
|
||||
depends on ARCH_S5PV210 || ARCH_EXYNOS
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_ALGAPI
|
||||
select CRYPTO_BLKCIPHER
|
||||
help
|
||||
This option allows you to have support for S5P crypto acceleration.
|
||||
@ -312,11 +347,13 @@ config CRYPTO_DEV_S5P
|
||||
algorithms execution.
|
||||
|
||||
config CRYPTO_DEV_NX
|
||||
bool "Support for IBM Power7+ in-Nest cryptographic acceleration"
|
||||
depends on PPC64 && IBMVIO && !CPU_LITTLE_ENDIAN
|
||||
default n
|
||||
bool "Support for IBM PowerPC Nest (NX) cryptographic acceleration"
|
||||
depends on PPC64
|
||||
help
|
||||
Support for Power7+ in-Nest cryptographic acceleration.
|
||||
This enables support for the NX hardware cryptographic accelerator
|
||||
coprocessor that is in IBM PowerPC P7+ or later processors. This
|
||||
does not actually enable any drivers, it only allows you to select
|
||||
which acceleration type (encryption and/or compression) to enable.
|
||||
|
||||
if CRYPTO_DEV_NX
|
||||
source "drivers/crypto/nx/Kconfig"
|
||||
@ -325,7 +362,6 @@ endif
|
||||
config CRYPTO_DEV_UX500
|
||||
tristate "Driver for ST-Ericsson UX500 crypto hardware acceleration"
|
||||
depends on ARCH_U8500
|
||||
select CRYPTO_ALGAPI
|
||||
help
|
||||
Driver for ST-Ericsson UX500 crypto engine.
|
||||
|
||||
@ -343,10 +379,7 @@ config CRYPTO_DEV_BFIN_CRC
|
||||
config CRYPTO_DEV_ATMEL_AES
|
||||
tristate "Support for Atmel AES hw accelerator"
|
||||
depends on ARCH_AT91
|
||||
select CRYPTO_CBC
|
||||
select CRYPTO_ECB
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_ALGAPI
|
||||
select CRYPTO_BLKCIPHER
|
||||
select AT_HDMAC
|
||||
help
|
||||
@ -361,9 +394,6 @@ config CRYPTO_DEV_ATMEL_TDES
|
||||
tristate "Support for Atmel DES/TDES hw accelerator"
|
||||
depends on ARCH_AT91
|
||||
select CRYPTO_DES
|
||||
select CRYPTO_CBC
|
||||
select CRYPTO_ECB
|
||||
select CRYPTO_ALGAPI
|
||||
select CRYPTO_BLKCIPHER
|
||||
help
|
||||
Some Atmel processors have DES/TDES hw accelerator.
|
||||
@ -376,10 +406,7 @@ config CRYPTO_DEV_ATMEL_TDES
|
||||
config CRYPTO_DEV_ATMEL_SHA
|
||||
tristate "Support for Atmel SHA hw accelerator"
|
||||
depends on ARCH_AT91
|
||||
select CRYPTO_SHA1
|
||||
select CRYPTO_SHA256
|
||||
select CRYPTO_SHA512
|
||||
select CRYPTO_ALGAPI
|
||||
select CRYPTO_HASH
|
||||
help
|
||||
Some Atmel processors have SHA1/SHA224/SHA256/SHA384/SHA512
|
||||
hw accelerator.
|
||||
@ -392,7 +419,6 @@ config CRYPTO_DEV_ATMEL_SHA
|
||||
config CRYPTO_DEV_CCP
|
||||
bool "Support for AMD Cryptographic Coprocessor"
|
||||
depends on ((X86 && PCI) || (ARM64 && (OF_ADDRESS || ACPI))) && HAS_IOMEM
|
||||
default n
|
||||
help
|
||||
The AMD Cryptographic Coprocessor provides hardware support
|
||||
for encryption, hashing and related operations.
|
||||
@ -404,13 +430,11 @@ endif
|
||||
config CRYPTO_DEV_MXS_DCP
|
||||
tristate "Support for Freescale MXS DCP"
|
||||
depends on ARCH_MXS
|
||||
select CRYPTO_SHA1
|
||||
select CRYPTO_SHA256
|
||||
select CRYPTO_CBC
|
||||
select CRYPTO_ECB
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_BLKCIPHER
|
||||
select CRYPTO_ALGAPI
|
||||
select CRYPTO_HASH
|
||||
help
|
||||
The Freescale i.MX23/i.MX28 has SHA1/SHA256 and AES128 CBC/ECB
|
||||
co-processor on the die.
|
||||
@ -429,7 +453,6 @@ config CRYPTO_DEV_QCE
|
||||
select CRYPTO_CBC
|
||||
select CRYPTO_XTS
|
||||
select CRYPTO_CTR
|
||||
select CRYPTO_ALGAPI
|
||||
select CRYPTO_BLKCIPHER
|
||||
help
|
||||
This driver supports Qualcomm crypto engine accelerator
|
||||
@ -439,7 +462,6 @@ config CRYPTO_DEV_QCE
|
||||
config CRYPTO_DEV_VMX
|
||||
bool "Support for VMX cryptographic acceleration instructions"
|
||||
depends on PPC64
|
||||
default n
|
||||
help
|
||||
Support for VMX cryptographic acceleration instructions.
|
||||
|
||||
@ -449,7 +471,6 @@ config CRYPTO_DEV_IMGTEC_HASH
|
||||
tristate "Imagination Technologies hardware hash accelerator"
|
||||
depends on MIPS || COMPILE_TEST
|
||||
depends on HAS_DMA
|
||||
select CRYPTO_ALGAPI
|
||||
select CRYPTO_MD5
|
||||
select CRYPTO_SHA1
|
||||
select CRYPTO_SHA256
|
||||
|
@ -9,6 +9,7 @@ obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o
|
||||
obj-$(CONFIG_CRYPTO_DEV_IMGTEC_HASH) += img-hash.o
|
||||
obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o
|
||||
obj-$(CONFIG_CRYPTO_DEV_MV_CESA) += mv_cesa.o
|
||||
obj-$(CONFIG_CRYPTO_DEV_MARVELL_CESA) += marvell/
|
||||
obj-$(CONFIG_CRYPTO_DEV_MXS_DCP) += mxs-dcp.o
|
||||
obj-$(CONFIG_CRYPTO_DEV_NIAGARA2) += n2_crypto.o
|
||||
n2_crypto-y := n2_core.o n2_asm.o
|
||||
|
@ -45,7 +45,6 @@ config CRYPTO_DEV_FSL_CAAM_RINGSIZE
|
||||
config CRYPTO_DEV_FSL_CAAM_INTC
|
||||
bool "Job Ring interrupt coalescing"
|
||||
depends on CRYPTO_DEV_FSL_CAAM_JR
|
||||
default n
|
||||
help
|
||||
Enable the Job Ring's interrupt coalescing feature.
|
||||
|
||||
@ -77,8 +76,9 @@ config CRYPTO_DEV_FSL_CAAM_CRYPTO_API
|
||||
tristate "Register algorithm implementations with the Crypto API"
|
||||
depends on CRYPTO_DEV_FSL_CAAM && CRYPTO_DEV_FSL_CAAM_JR
|
||||
default y
|
||||
select CRYPTO_ALGAPI
|
||||
select CRYPTO_AEAD
|
||||
select CRYPTO_AUTHENC
|
||||
select CRYPTO_BLKCIPHER
|
||||
help
|
||||
Selecting this will offload crypto for users of the
|
||||
scatterlist crypto API (such as the linux native IPSec
|
||||
@ -115,7 +115,6 @@ config CRYPTO_DEV_FSL_CAAM_RNG_API
|
||||
config CRYPTO_DEV_FSL_CAAM_DEBUG
|
||||
bool "Enable debug output in CAAM driver"
|
||||
depends on CRYPTO_DEV_FSL_CAAM
|
||||
default n
|
||||
help
|
||||
Selecting this will enable printing of various debug
|
||||
information in the CAAM driver.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -835,17 +835,17 @@ static int ahash_update_ctx(struct ahash_request *req)
|
||||
src_map_to_sec4_sg(jrdev, req->src, src_nents,
|
||||
edesc->sec4_sg + sec4_sg_src_index,
|
||||
chained);
|
||||
if (*next_buflen) {
|
||||
if (*next_buflen)
|
||||
scatterwalk_map_and_copy(next_buf, req->src,
|
||||
to_hash - *buflen,
|
||||
*next_buflen, 0);
|
||||
state->current_buf = !state->current_buf;
|
||||
}
|
||||
} else {
|
||||
(edesc->sec4_sg + sec4_sg_src_index - 1)->len |=
|
||||
SEC4_SG_LEN_FIN;
|
||||
}
|
||||
|
||||
state->current_buf = !state->current_buf;
|
||||
|
||||
sh_len = desc_len(sh_desc);
|
||||
desc = edesc->hw_desc;
|
||||
init_job_desc_shared(desc, ptr, sh_len, HDR_SHARE_DEFER |
|
||||
@ -1268,9 +1268,10 @@ static int ahash_update_no_ctx(struct ahash_request *req)
|
||||
scatterwalk_map_and_copy(next_buf, req->src,
|
||||
to_hash - *buflen,
|
||||
*next_buflen, 0);
|
||||
state->current_buf = !state->current_buf;
|
||||
}
|
||||
|
||||
state->current_buf = !state->current_buf;
|
||||
|
||||
sh_len = desc_len(sh_desc);
|
||||
desc = edesc->hw_desc;
|
||||
init_job_desc_shared(desc, ptr, sh_len, HDR_SHARE_DEFER |
|
||||
|
@ -32,7 +32,7 @@
|
||||
#include <crypto/des.h>
|
||||
#include <crypto/sha.h>
|
||||
#include <crypto/md5.h>
|
||||
#include <crypto/aead.h>
|
||||
#include <crypto/internal/aead.h>
|
||||
#include <crypto/authenc.h>
|
||||
#include <crypto/scatterwalk.h>
|
||||
#include <crypto/internal/skcipher.h>
|
||||
|
@ -301,7 +301,7 @@ static int caam_remove(struct platform_device *pdev)
|
||||
#endif
|
||||
|
||||
/* Unmap controller region */
|
||||
iounmap(&ctrl);
|
||||
iounmap(ctrl);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -496,7 +496,7 @@ static int caam_probe(struct platform_device *pdev)
|
||||
sizeof(struct platform_device *) * rspec,
|
||||
GFP_KERNEL);
|
||||
if (ctrlpriv->jrpdev == NULL) {
|
||||
iounmap(&ctrl);
|
||||
iounmap(ctrl);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
@ -83,34 +83,34 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The only users of these wr/rd_reg64 functions is the Job Ring (JR).
|
||||
* The DMA address registers in the JR are a pair of 32-bit registers.
|
||||
* The layout is:
|
||||
*
|
||||
* base + 0x0000 : most-significant 32 bits
|
||||
* base + 0x0004 : least-significant 32 bits
|
||||
*
|
||||
* The 32-bit version of this core therefore has to write to base + 0x0004
|
||||
* to set the 32-bit wide DMA address. This seems to be independent of the
|
||||
* endianness of the written/read data.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_64BIT
|
||||
#ifdef __BIG_ENDIAN
|
||||
#define REG64_MS32(reg) ((u32 __iomem *)(reg))
|
||||
#define REG64_LS32(reg) ((u32 __iomem *)(reg) + 1)
|
||||
|
||||
static inline void wr_reg64(u64 __iomem *reg, u64 data)
|
||||
{
|
||||
wr_reg32((u32 __iomem *)reg, (data & 0xffffffff00000000ull) >> 32);
|
||||
wr_reg32((u32 __iomem *)reg + 1, data & 0x00000000ffffffffull);
|
||||
wr_reg32(REG64_MS32(reg), data >> 32);
|
||||
wr_reg32(REG64_LS32(reg), data);
|
||||
}
|
||||
|
||||
static inline u64 rd_reg64(u64 __iomem *reg)
|
||||
{
|
||||
return (((u64)rd_reg32((u32 __iomem *)reg)) << 32) |
|
||||
((u64)rd_reg32((u32 __iomem *)reg + 1));
|
||||
return ((u64)rd_reg32(REG64_MS32(reg)) << 32 |
|
||||
(u64)rd_reg32(REG64_LS32(reg)));
|
||||
}
|
||||
#else
|
||||
#ifdef __LITTLE_ENDIAN
|
||||
static inline void wr_reg64(u64 __iomem *reg, u64 data)
|
||||
{
|
||||
wr_reg32((u32 __iomem *)reg + 1, (data & 0xffffffff00000000ull) >> 32);
|
||||
wr_reg32((u32 __iomem *)reg, data & 0x00000000ffffffffull);
|
||||
}
|
||||
|
||||
static inline u64 rd_reg64(u64 __iomem *reg)
|
||||
{
|
||||
return (((u64)rd_reg32((u32 __iomem *)reg + 1)) << 32) |
|
||||
((u64)rd_reg32((u32 __iomem *)reg));
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -55,6 +55,21 @@ static inline void sg_to_sec4_sg_last(struct scatterlist *sg, int sg_count,
|
||||
sec4_sg_ptr->len |= SEC4_SG_LEN_FIN;
|
||||
}
|
||||
|
||||
static inline struct sec4_sg_entry *sg_to_sec4_sg_len(
|
||||
struct scatterlist *sg, unsigned int total,
|
||||
struct sec4_sg_entry *sec4_sg_ptr)
|
||||
{
|
||||
do {
|
||||
unsigned int len = min(sg_dma_len(sg), total);
|
||||
|
||||
dma_to_sec4_sg_one(sec4_sg_ptr, sg_dma_address(sg), len, 0);
|
||||
sec4_sg_ptr++;
|
||||
sg = sg_next(sg);
|
||||
total -= len;
|
||||
} while (total);
|
||||
return sec4_sg_ptr - 1;
|
||||
}
|
||||
|
||||
/* count number of elements in scatterlist */
|
||||
static inline int __sg_count(struct scatterlist *sg_list, int nbytes,
|
||||
bool *chained)
|
||||
@ -85,25 +100,9 @@ static inline int sg_count(struct scatterlist *sg_list, int nbytes,
|
||||
return sg_nents;
|
||||
}
|
||||
|
||||
static int dma_map_sg_chained(struct device *dev, struct scatterlist *sg,
|
||||
unsigned int nents, enum dma_data_direction dir,
|
||||
bool chained)
|
||||
{
|
||||
if (unlikely(chained)) {
|
||||
int i;
|
||||
for (i = 0; i < nents; i++) {
|
||||
dma_map_sg(dev, sg, 1, dir);
|
||||
sg = sg_next(sg);
|
||||
}
|
||||
} else {
|
||||
dma_map_sg(dev, sg, nents, dir);
|
||||
}
|
||||
return nents;
|
||||
}
|
||||
|
||||
static int dma_unmap_sg_chained(struct device *dev, struct scatterlist *sg,
|
||||
unsigned int nents, enum dma_data_direction dir,
|
||||
bool chained)
|
||||
static inline void dma_unmap_sg_chained(
|
||||
struct device *dev, struct scatterlist *sg, unsigned int nents,
|
||||
enum dma_data_direction dir, bool chained)
|
||||
{
|
||||
if (unlikely(chained)) {
|
||||
int i;
|
||||
@ -111,8 +110,31 @@ static int dma_unmap_sg_chained(struct device *dev, struct scatterlist *sg,
|
||||
dma_unmap_sg(dev, sg, 1, dir);
|
||||
sg = sg_next(sg);
|
||||
}
|
||||
} else {
|
||||
} else if (nents) {
|
||||
dma_unmap_sg(dev, sg, nents, dir);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int dma_map_sg_chained(
|
||||
struct device *dev, struct scatterlist *sg, unsigned int nents,
|
||||
enum dma_data_direction dir, bool chained)
|
||||
{
|
||||
struct scatterlist *first = sg;
|
||||
|
||||
if (unlikely(chained)) {
|
||||
int i;
|
||||
for (i = 0; i < nents; i++) {
|
||||
if (!dma_map_sg(dev, sg, 1, dir)) {
|
||||
dma_unmap_sg_chained(dev, first, i, dir,
|
||||
chained);
|
||||
nents = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
sg = sg_next(sg);
|
||||
}
|
||||
} else
|
||||
nents = dma_map_sg(dev, sg, nents, dir);
|
||||
|
||||
return nents;
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ config CRYPTO_DEV_CCP_CRYPTO
|
||||
tristate "Encryption and hashing acceleration support"
|
||||
depends on CRYPTO_DEV_CCP_DD
|
||||
default m
|
||||
select CRYPTO_ALGAPI
|
||||
select CRYPTO_HASH
|
||||
select CRYPTO_BLKCIPHER
|
||||
select CRYPTO_AUTHENC
|
||||
|
@ -52,8 +52,7 @@ struct ccp_dm_workarea {
|
||||
|
||||
struct ccp_sg_workarea {
|
||||
struct scatterlist *sg;
|
||||
unsigned int nents;
|
||||
unsigned int length;
|
||||
int nents;
|
||||
|
||||
struct scatterlist *dma_sg;
|
||||
struct device *dma_dev;
|
||||
@ -496,8 +495,10 @@ static int ccp_init_sg_workarea(struct ccp_sg_workarea *wa, struct device *dev,
|
||||
if (!sg)
|
||||
return 0;
|
||||
|
||||
wa->nents = sg_nents(sg);
|
||||
wa->length = sg->length;
|
||||
wa->nents = sg_nents_for_len(sg, len);
|
||||
if (wa->nents < 0)
|
||||
return wa->nents;
|
||||
|
||||
wa->bytes_left = len;
|
||||
wa->sg_used = 0;
|
||||
|
||||
|
@ -174,8 +174,6 @@ static int ccp_platform_probe(struct platform_device *pdev)
|
||||
}
|
||||
ccp->io_regs = ccp->io_map;
|
||||
|
||||
if (!dev->dma_mask)
|
||||
dev->dma_mask = &dev->coherent_dma_mask;
|
||||
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));
|
||||
if (ret) {
|
||||
dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n", ret);
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include <crypto/aes.h>
|
||||
#include <crypto/sha.h>
|
||||
#include <crypto/algapi.h>
|
||||
#include <crypto/aead.h>
|
||||
#include <crypto/internal/aead.h>
|
||||
#include <crypto/authenc.h>
|
||||
#include <crypto/scatterwalk.h>
|
||||
|
||||
@ -575,7 +575,8 @@ static int init_tfm_ablk(struct crypto_tfm *tfm)
|
||||
|
||||
static int init_tfm_aead(struct crypto_tfm *tfm)
|
||||
{
|
||||
tfm->crt_aead.reqsize = sizeof(struct aead_ctx);
|
||||
crypto_aead_set_reqsize(__crypto_aead_cast(tfm),
|
||||
sizeof(struct aead_ctx));
|
||||
return init_tfm(tfm);
|
||||
}
|
||||
|
||||
@ -1096,7 +1097,7 @@ static int aead_setup(struct crypto_aead *tfm, unsigned int authsize)
|
||||
{
|
||||
struct ixp_ctx *ctx = crypto_aead_ctx(tfm);
|
||||
u32 *flags = &tfm->base.crt_flags;
|
||||
unsigned digest_len = crypto_aead_alg(tfm)->maxauthsize;
|
||||
unsigned digest_len = crypto_aead_maxauthsize(tfm);
|
||||
int ret;
|
||||
|
||||
if (!ctx->enckey_len && !ctx->authkey_len)
|
||||
@ -1138,7 +1139,7 @@ out:
|
||||
|
||||
static int aead_setauthsize(struct crypto_aead *tfm, unsigned int authsize)
|
||||
{
|
||||
int max = crypto_aead_alg(tfm)->maxauthsize >> 2;
|
||||
int max = crypto_aead_maxauthsize(tfm) >> 2;
|
||||
|
||||
if ((authsize>>2) < 1 || (authsize>>2) > max || (authsize & 3))
|
||||
return -EINVAL;
|
||||
|
2
drivers/crypto/marvell/Makefile
Normal file
2
drivers/crypto/marvell/Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
obj-$(CONFIG_CRYPTO_DEV_MARVELL_CESA) += marvell-cesa.o
|
||||
marvell-cesa-objs := cesa.o cipher.o hash.o tdma.o
|
548
drivers/crypto/marvell/cesa.c
Normal file
548
drivers/crypto/marvell/cesa.c
Normal file
@ -0,0 +1,548 @@
|
||||
/*
|
||||
* Support for Marvell's Cryptographic Engine and Security Accelerator (CESA)
|
||||
* that can be found on the following platform: Orion, Kirkwood, Armada. This
|
||||
* driver supports the TDMA engine on platforms on which it is available.
|
||||
*
|
||||
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
|
||||
* Author: Arnaud Ebalard <arno@natisbad.org>
|
||||
*
|
||||
* This work is based on an initial version written by
|
||||
* Sebastian Andrzej Siewior < sebastian at breakpoint dot cc >
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/mbus.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_irq.h>
|
||||
|
||||
#include "cesa.h"
|
||||
|
||||
static int allhwsupport = !IS_ENABLED(CONFIG_CRYPTO_DEV_MV_CESA);
|
||||
module_param_named(allhwsupport, allhwsupport, int, 0444);
|
||||
MODULE_PARM_DESC(allhwsupport, "Enable support for all hardware (even it if overlaps with the mv_cesa driver)");
|
||||
|
||||
struct mv_cesa_dev *cesa_dev;
|
||||
|
||||
static void mv_cesa_dequeue_req_unlocked(struct mv_cesa_engine *engine)
|
||||
{
|
||||
struct crypto_async_request *req, *backlog;
|
||||
struct mv_cesa_ctx *ctx;
|
||||
|
||||
spin_lock_bh(&cesa_dev->lock);
|
||||
backlog = crypto_get_backlog(&cesa_dev->queue);
|
||||
req = crypto_dequeue_request(&cesa_dev->queue);
|
||||
engine->req = req;
|
||||
spin_unlock_bh(&cesa_dev->lock);
|
||||
|
||||
if (!req)
|
||||
return;
|
||||
|
||||
if (backlog)
|
||||
backlog->complete(backlog, -EINPROGRESS);
|
||||
|
||||
ctx = crypto_tfm_ctx(req->tfm);
|
||||
ctx->ops->prepare(req, engine);
|
||||
ctx->ops->step(req);
|
||||
}
|
||||
|
||||
static irqreturn_t mv_cesa_int(int irq, void *priv)
|
||||
{
|
||||
struct mv_cesa_engine *engine = priv;
|
||||
struct crypto_async_request *req;
|
||||
struct mv_cesa_ctx *ctx;
|
||||
u32 status, mask;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
while (true) {
|
||||
int res;
|
||||
|
||||
mask = mv_cesa_get_int_mask(engine);
|
||||
status = readl(engine->regs + CESA_SA_INT_STATUS);
|
||||
|
||||
if (!(status & mask))
|
||||
break;
|
||||
|
||||
/*
|
||||
* TODO: avoid clearing the FPGA_INT_STATUS if this not
|
||||
* relevant on some platforms.
|
||||
*/
|
||||
writel(~status, engine->regs + CESA_SA_FPGA_INT_STATUS);
|
||||
writel(~status, engine->regs + CESA_SA_INT_STATUS);
|
||||
|
||||
ret = IRQ_HANDLED;
|
||||
spin_lock_bh(&engine->lock);
|
||||
req = engine->req;
|
||||
spin_unlock_bh(&engine->lock);
|
||||
if (req) {
|
||||
ctx = crypto_tfm_ctx(req->tfm);
|
||||
res = ctx->ops->process(req, status & mask);
|
||||
if (res != -EINPROGRESS) {
|
||||
spin_lock_bh(&engine->lock);
|
||||
engine->req = NULL;
|
||||
mv_cesa_dequeue_req_unlocked(engine);
|
||||
spin_unlock_bh(&engine->lock);
|
||||
ctx->ops->cleanup(req);
|
||||
local_bh_disable();
|
||||
req->complete(req, res);
|
||||
local_bh_enable();
|
||||
} else {
|
||||
ctx->ops->step(req);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mv_cesa_queue_req(struct crypto_async_request *req)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
spin_lock_bh(&cesa_dev->lock);
|
||||
ret = crypto_enqueue_request(&cesa_dev->queue, req);
|
||||
spin_unlock_bh(&cesa_dev->lock);
|
||||
|
||||
if (ret != -EINPROGRESS)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < cesa_dev->caps->nengines; i++) {
|
||||
spin_lock_bh(&cesa_dev->engines[i].lock);
|
||||
if (!cesa_dev->engines[i].req)
|
||||
mv_cesa_dequeue_req_unlocked(&cesa_dev->engines[i]);
|
||||
spin_unlock_bh(&cesa_dev->engines[i].lock);
|
||||
}
|
||||
|
||||
return -EINPROGRESS;
|
||||
}
|
||||
|
||||
static int mv_cesa_add_algs(struct mv_cesa_dev *cesa)
|
||||
{
|
||||
int ret;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < cesa->caps->ncipher_algs; i++) {
|
||||
ret = crypto_register_alg(cesa->caps->cipher_algs[i]);
|
||||
if (ret)
|
||||
goto err_unregister_crypto;
|
||||
}
|
||||
|
||||
for (i = 0; i < cesa->caps->nahash_algs; i++) {
|
||||
ret = crypto_register_ahash(cesa->caps->ahash_algs[i]);
|
||||
if (ret)
|
||||
goto err_unregister_ahash;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_ahash:
|
||||
for (j = 0; j < i; j++)
|
||||
crypto_unregister_ahash(cesa->caps->ahash_algs[j]);
|
||||
i = cesa->caps->ncipher_algs;
|
||||
|
||||
err_unregister_crypto:
|
||||
for (j = 0; j < i; j++)
|
||||
crypto_unregister_alg(cesa->caps->cipher_algs[j]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mv_cesa_remove_algs(struct mv_cesa_dev *cesa)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cesa->caps->nahash_algs; i++)
|
||||
crypto_unregister_ahash(cesa->caps->ahash_algs[i]);
|
||||
|
||||
for (i = 0; i < cesa->caps->ncipher_algs; i++)
|
||||
crypto_unregister_alg(cesa->caps->cipher_algs[i]);
|
||||
}
|
||||
|
||||
static struct crypto_alg *orion_cipher_algs[] = {
|
||||
&mv_cesa_ecb_des_alg,
|
||||
&mv_cesa_cbc_des_alg,
|
||||
&mv_cesa_ecb_des3_ede_alg,
|
||||
&mv_cesa_cbc_des3_ede_alg,
|
||||
&mv_cesa_ecb_aes_alg,
|
||||
&mv_cesa_cbc_aes_alg,
|
||||
};
|
||||
|
||||
static struct ahash_alg *orion_ahash_algs[] = {
|
||||
&mv_md5_alg,
|
||||
&mv_sha1_alg,
|
||||
&mv_ahmac_md5_alg,
|
||||
&mv_ahmac_sha1_alg,
|
||||
};
|
||||
|
||||
static struct crypto_alg *armada_370_cipher_algs[] = {
|
||||
&mv_cesa_ecb_des_alg,
|
||||
&mv_cesa_cbc_des_alg,
|
||||
&mv_cesa_ecb_des3_ede_alg,
|
||||
&mv_cesa_cbc_des3_ede_alg,
|
||||
&mv_cesa_ecb_aes_alg,
|
||||
&mv_cesa_cbc_aes_alg,
|
||||
};
|
||||
|
||||
static struct ahash_alg *armada_370_ahash_algs[] = {
|
||||
&mv_md5_alg,
|
||||
&mv_sha1_alg,
|
||||
&mv_sha256_alg,
|
||||
&mv_ahmac_md5_alg,
|
||||
&mv_ahmac_sha1_alg,
|
||||
&mv_ahmac_sha256_alg,
|
||||
};
|
||||
|
||||
static const struct mv_cesa_caps orion_caps = {
|
||||
.nengines = 1,
|
||||
.cipher_algs = orion_cipher_algs,
|
||||
.ncipher_algs = ARRAY_SIZE(orion_cipher_algs),
|
||||
.ahash_algs = orion_ahash_algs,
|
||||
.nahash_algs = ARRAY_SIZE(orion_ahash_algs),
|
||||
.has_tdma = false,
|
||||
};
|
||||
|
||||
static const struct mv_cesa_caps kirkwood_caps = {
|
||||
.nengines = 1,
|
||||
.cipher_algs = orion_cipher_algs,
|
||||
.ncipher_algs = ARRAY_SIZE(orion_cipher_algs),
|
||||
.ahash_algs = orion_ahash_algs,
|
||||
.nahash_algs = ARRAY_SIZE(orion_ahash_algs),
|
||||
.has_tdma = true,
|
||||
};
|
||||
|
||||
static const struct mv_cesa_caps armada_370_caps = {
|
||||
.nengines = 1,
|
||||
.cipher_algs = armada_370_cipher_algs,
|
||||
.ncipher_algs = ARRAY_SIZE(armada_370_cipher_algs),
|
||||
.ahash_algs = armada_370_ahash_algs,
|
||||
.nahash_algs = ARRAY_SIZE(armada_370_ahash_algs),
|
||||
.has_tdma = true,
|
||||
};
|
||||
|
||||
static const struct mv_cesa_caps armada_xp_caps = {
|
||||
.nengines = 2,
|
||||
.cipher_algs = armada_370_cipher_algs,
|
||||
.ncipher_algs = ARRAY_SIZE(armada_370_cipher_algs),
|
||||
.ahash_algs = armada_370_ahash_algs,
|
||||
.nahash_algs = ARRAY_SIZE(armada_370_ahash_algs),
|
||||
.has_tdma = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id mv_cesa_of_match_table[] = {
|
||||
{ .compatible = "marvell,orion-crypto", .data = &orion_caps },
|
||||
{ .compatible = "marvell,kirkwood-crypto", .data = &kirkwood_caps },
|
||||
{ .compatible = "marvell,dove-crypto", .data = &kirkwood_caps },
|
||||
{ .compatible = "marvell,armada-370-crypto", .data = &armada_370_caps },
|
||||
{ .compatible = "marvell,armada-xp-crypto", .data = &armada_xp_caps },
|
||||
{ .compatible = "marvell,armada-375-crypto", .data = &armada_xp_caps },
|
||||
{ .compatible = "marvell,armada-38x-crypto", .data = &armada_xp_caps },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mv_cesa_of_match_table);
|
||||
|
||||
static void
|
||||
mv_cesa_conf_mbus_windows(struct mv_cesa_engine *engine,
|
||||
const struct mbus_dram_target_info *dram)
|
||||
{
|
||||
void __iomem *iobase = engine->regs;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
writel(0, iobase + CESA_TDMA_WINDOW_CTRL(i));
|
||||
writel(0, iobase + CESA_TDMA_WINDOW_BASE(i));
|
||||
}
|
||||
|
||||
for (i = 0; i < dram->num_cs; i++) {
|
||||
const struct mbus_dram_window *cs = dram->cs + i;
|
||||
|
||||
writel(((cs->size - 1) & 0xffff0000) |
|
||||
(cs->mbus_attr << 8) |
|
||||
(dram->mbus_dram_target_id << 4) | 1,
|
||||
iobase + CESA_TDMA_WINDOW_CTRL(i));
|
||||
writel(cs->base, iobase + CESA_TDMA_WINDOW_BASE(i));
|
||||
}
|
||||
}
|
||||
|
||||
static int mv_cesa_dev_dma_init(struct mv_cesa_dev *cesa)
|
||||
{
|
||||
struct device *dev = cesa->dev;
|
||||
struct mv_cesa_dev_dma *dma;
|
||||
|
||||
if (!cesa->caps->has_tdma)
|
||||
return 0;
|
||||
|
||||
dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
|
||||
if (!dma)
|
||||
return -ENOMEM;
|
||||
|
||||
dma->tdma_desc_pool = dmam_pool_create("tdma_desc", dev,
|
||||
sizeof(struct mv_cesa_tdma_desc),
|
||||
16, 0);
|
||||
if (!dma->tdma_desc_pool)
|
||||
return -ENOMEM;
|
||||
|
||||
dma->op_pool = dmam_pool_create("cesa_op", dev,
|
||||
sizeof(struct mv_cesa_op_ctx), 16, 0);
|
||||
if (!dma->op_pool)
|
||||
return -ENOMEM;
|
||||
|
||||
dma->cache_pool = dmam_pool_create("cesa_cache", dev,
|
||||
CESA_MAX_HASH_BLOCK_SIZE, 1, 0);
|
||||
if (!dma->cache_pool)
|
||||
return -ENOMEM;
|
||||
|
||||
dma->padding_pool = dmam_pool_create("cesa_padding", dev, 72, 1, 0);
|
||||
if (!dma->cache_pool)
|
||||
return -ENOMEM;
|
||||
|
||||
cesa->dma = dma;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_cesa_get_sram(struct platform_device *pdev, int idx)
|
||||
{
|
||||
struct mv_cesa_dev *cesa = platform_get_drvdata(pdev);
|
||||
struct mv_cesa_engine *engine = &cesa->engines[idx];
|
||||
const char *res_name = "sram";
|
||||
struct resource *res;
|
||||
|
||||
engine->pool = of_get_named_gen_pool(cesa->dev->of_node,
|
||||
"marvell,crypto-srams",
|
||||
idx);
|
||||
if (engine->pool) {
|
||||
engine->sram = gen_pool_dma_alloc(engine->pool,
|
||||
cesa->sram_size,
|
||||
&engine->sram_dma);
|
||||
if (engine->sram)
|
||||
return 0;
|
||||
|
||||
engine->pool = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (cesa->caps->nengines > 1) {
|
||||
if (!idx)
|
||||
res_name = "sram0";
|
||||
else
|
||||
res_name = "sram1";
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
res_name);
|
||||
if (!res || resource_size(res) < cesa->sram_size)
|
||||
return -EINVAL;
|
||||
|
||||
engine->sram = devm_ioremap_resource(cesa->dev, res);
|
||||
if (IS_ERR(engine->sram))
|
||||
return PTR_ERR(engine->sram);
|
||||
|
||||
engine->sram_dma = phys_to_dma(cesa->dev,
|
||||
(phys_addr_t)res->start);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mv_cesa_put_sram(struct platform_device *pdev, int idx)
|
||||
{
|
||||
struct mv_cesa_dev *cesa = platform_get_drvdata(pdev);
|
||||
struct mv_cesa_engine *engine = &cesa->engines[idx];
|
||||
|
||||
if (!engine->pool)
|
||||
return;
|
||||
|
||||
gen_pool_free(engine->pool, (unsigned long)engine->sram,
|
||||
cesa->sram_size);
|
||||
}
|
||||
|
||||
static int mv_cesa_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct mv_cesa_caps *caps = &orion_caps;
|
||||
const struct mbus_dram_target_info *dram;
|
||||
const struct of_device_id *match;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct mv_cesa_dev *cesa;
|
||||
struct mv_cesa_engine *engines;
|
||||
struct resource *res;
|
||||
int irq, ret, i;
|
||||
u32 sram_size;
|
||||
|
||||
if (cesa_dev) {
|
||||
dev_err(&pdev->dev, "Only one CESA device authorized\n");
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
if (dev->of_node) {
|
||||
match = of_match_node(mv_cesa_of_match_table, dev->of_node);
|
||||
if (!match || !match->data)
|
||||
return -ENOTSUPP;
|
||||
|
||||
caps = match->data;
|
||||
}
|
||||
|
||||
if ((caps == &orion_caps || caps == &kirkwood_caps) && !allhwsupport)
|
||||
return -ENOTSUPP;
|
||||
|
||||
cesa = devm_kzalloc(dev, sizeof(*cesa), GFP_KERNEL);
|
||||
if (!cesa)
|
||||
return -ENOMEM;
|
||||
|
||||
cesa->caps = caps;
|
||||
cesa->dev = dev;
|
||||
|
||||
sram_size = CESA_SA_DEFAULT_SRAM_SIZE;
|
||||
of_property_read_u32(cesa->dev->of_node, "marvell,crypto-sram-size",
|
||||
&sram_size);
|
||||
if (sram_size < CESA_SA_MIN_SRAM_SIZE)
|
||||
sram_size = CESA_SA_MIN_SRAM_SIZE;
|
||||
|
||||
cesa->sram_size = sram_size;
|
||||
cesa->engines = devm_kzalloc(dev, caps->nengines * sizeof(*engines),
|
||||
GFP_KERNEL);
|
||||
if (!cesa->engines)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&cesa->lock);
|
||||
crypto_init_queue(&cesa->queue, 50);
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
|
||||
cesa->regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(cesa->regs))
|
||||
return -ENOMEM;
|
||||
|
||||
ret = mv_cesa_dev_dma_init(cesa);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dram = mv_mbus_dram_info_nooverlap();
|
||||
|
||||
platform_set_drvdata(pdev, cesa);
|
||||
|
||||
for (i = 0; i < caps->nengines; i++) {
|
||||
struct mv_cesa_engine *engine = &cesa->engines[i];
|
||||
char res_name[7];
|
||||
|
||||
engine->id = i;
|
||||
spin_lock_init(&engine->lock);
|
||||
|
||||
ret = mv_cesa_get_sram(pdev, i);
|
||||
if (ret)
|
||||
goto err_cleanup;
|
||||
|
||||
irq = platform_get_irq(pdev, i);
|
||||
if (irq < 0) {
|
||||
ret = irq;
|
||||
goto err_cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Not all platforms can gate the CESA clocks: do not complain
|
||||
* if the clock does not exist.
|
||||
*/
|
||||
snprintf(res_name, sizeof(res_name), "cesa%d", i);
|
||||
engine->clk = devm_clk_get(dev, res_name);
|
||||
if (IS_ERR(engine->clk)) {
|
||||
engine->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(engine->clk))
|
||||
engine->clk = NULL;
|
||||
}
|
||||
|
||||
snprintf(res_name, sizeof(res_name), "cesaz%d", i);
|
||||
engine->zclk = devm_clk_get(dev, res_name);
|
||||
if (IS_ERR(engine->zclk))
|
||||
engine->zclk = NULL;
|
||||
|
||||
ret = clk_prepare_enable(engine->clk);
|
||||
if (ret)
|
||||
goto err_cleanup;
|
||||
|
||||
ret = clk_prepare_enable(engine->zclk);
|
||||
if (ret)
|
||||
goto err_cleanup;
|
||||
|
||||
engine->regs = cesa->regs + CESA_ENGINE_OFF(i);
|
||||
|
||||
if (dram && cesa->caps->has_tdma)
|
||||
mv_cesa_conf_mbus_windows(&cesa->engines[i], dram);
|
||||
|
||||
writel(0, cesa->engines[i].regs + CESA_SA_INT_STATUS);
|
||||
writel(CESA_SA_CFG_STOP_DIG_ERR,
|
||||
cesa->engines[i].regs + CESA_SA_CFG);
|
||||
writel(engine->sram_dma & CESA_SA_SRAM_MSK,
|
||||
cesa->engines[i].regs + CESA_SA_DESC_P0);
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL, mv_cesa_int,
|
||||
IRQF_ONESHOT,
|
||||
dev_name(&pdev->dev),
|
||||
&cesa->engines[i]);
|
||||
if (ret)
|
||||
goto err_cleanup;
|
||||
}
|
||||
|
||||
cesa_dev = cesa;
|
||||
|
||||
ret = mv_cesa_add_algs(cesa);
|
||||
if (ret) {
|
||||
cesa_dev = NULL;
|
||||
goto err_cleanup;
|
||||
}
|
||||
|
||||
dev_info(dev, "CESA device successfully registered\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_cleanup:
|
||||
for (i = 0; i < caps->nengines; i++) {
|
||||
clk_disable_unprepare(cesa->engines[i].zclk);
|
||||
clk_disable_unprepare(cesa->engines[i].clk);
|
||||
mv_cesa_put_sram(pdev, i);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mv_cesa_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mv_cesa_dev *cesa = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
mv_cesa_remove_algs(cesa);
|
||||
|
||||
for (i = 0; i < cesa->caps->nengines; i++) {
|
||||
clk_disable_unprepare(cesa->engines[i].zclk);
|
||||
clk_disable_unprepare(cesa->engines[i].clk);
|
||||
mv_cesa_put_sram(pdev, i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver marvell_cesa = {
|
||||
.probe = mv_cesa_probe,
|
||||
.remove = mv_cesa_remove,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "marvell-cesa",
|
||||
.of_match_table = mv_cesa_of_match_table,
|
||||
},
|
||||
};
|
||||
module_platform_driver(marvell_cesa);
|
||||
|
||||
MODULE_ALIAS("platform:mv_crypto");
|
||||
MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
|
||||
MODULE_AUTHOR("Arnaud Ebalard <arno@natisbad.org>");
|
||||
MODULE_DESCRIPTION("Support for Marvell's cryptographic engine");
|
||||
MODULE_LICENSE("GPL v2");
|
791
drivers/crypto/marvell/cesa.h
Normal file
791
drivers/crypto/marvell/cesa.h
Normal file
@ -0,0 +1,791 @@
|
||||
#ifndef __MARVELL_CESA_H__
|
||||
#define __MARVELL_CESA_H__
|
||||
|
||||
#include <crypto/algapi.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/internal/hash.h>
|
||||
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/dmapool.h>
|
||||
|
||||
#define CESA_ENGINE_OFF(i) (((i) * 0x2000))
|
||||
|
||||
#define CESA_TDMA_BYTE_CNT 0x800
|
||||
#define CESA_TDMA_SRC_ADDR 0x810
|
||||
#define CESA_TDMA_DST_ADDR 0x820
|
||||
#define CESA_TDMA_NEXT_ADDR 0x830
|
||||
|
||||
#define CESA_TDMA_CONTROL 0x840
|
||||
#define CESA_TDMA_DST_BURST GENMASK(2, 0)
|
||||
#define CESA_TDMA_DST_BURST_32B 3
|
||||
#define CESA_TDMA_DST_BURST_128B 4
|
||||
#define CESA_TDMA_OUT_RD_EN BIT(4)
|
||||
#define CESA_TDMA_SRC_BURST GENMASK(8, 6)
|
||||
#define CESA_TDMA_SRC_BURST_32B (3 << 6)
|
||||
#define CESA_TDMA_SRC_BURST_128B (4 << 6)
|
||||
#define CESA_TDMA_CHAIN BIT(9)
|
||||
#define CESA_TDMA_BYTE_SWAP BIT(11)
|
||||
#define CESA_TDMA_NO_BYTE_SWAP BIT(11)
|
||||
#define CESA_TDMA_EN BIT(12)
|
||||
#define CESA_TDMA_FETCH_ND BIT(13)
|
||||
#define CESA_TDMA_ACT BIT(14)
|
||||
|
||||
#define CESA_TDMA_CUR 0x870
|
||||
#define CESA_TDMA_ERROR_CAUSE 0x8c8
|
||||
#define CESA_TDMA_ERROR_MSK 0x8cc
|
||||
|
||||
#define CESA_TDMA_WINDOW_BASE(x) (((x) * 0x8) + 0xa00)
|
||||
#define CESA_TDMA_WINDOW_CTRL(x) (((x) * 0x8) + 0xa04)
|
||||
|
||||
#define CESA_IVDIG(x) (0xdd00 + ((x) * 4) + \
|
||||
(((x) < 5) ? 0 : 0x14))
|
||||
|
||||
#define CESA_SA_CMD 0xde00
|
||||
#define CESA_SA_CMD_EN_CESA_SA_ACCL0 BIT(0)
|
||||
#define CESA_SA_CMD_EN_CESA_SA_ACCL1 BIT(1)
|
||||
#define CESA_SA_CMD_DISABLE_SEC BIT(2)
|
||||
|
||||
#define CESA_SA_DESC_P0 0xde04
|
||||
|
||||
#define CESA_SA_DESC_P1 0xde14
|
||||
|
||||
#define CESA_SA_CFG 0xde08
|
||||
#define CESA_SA_CFG_STOP_DIG_ERR GENMASK(1, 0)
|
||||
#define CESA_SA_CFG_DIG_ERR_CONT 0
|
||||
#define CESA_SA_CFG_DIG_ERR_SKIP 1
|
||||
#define CESA_SA_CFG_DIG_ERR_STOP 3
|
||||
#define CESA_SA_CFG_CH0_W_IDMA BIT(7)
|
||||
#define CESA_SA_CFG_CH1_W_IDMA BIT(8)
|
||||
#define CESA_SA_CFG_ACT_CH0_IDMA BIT(9)
|
||||
#define CESA_SA_CFG_ACT_CH1_IDMA BIT(10)
|
||||
#define CESA_SA_CFG_MULTI_PKT BIT(11)
|
||||
#define CESA_SA_CFG_PARA_DIS BIT(13)
|
||||
|
||||
#define CESA_SA_ACCEL_STATUS 0xde0c
|
||||
#define CESA_SA_ST_ACT_0 BIT(0)
|
||||
#define CESA_SA_ST_ACT_1 BIT(1)
|
||||
|
||||
/*
|
||||
* CESA_SA_FPGA_INT_STATUS looks like a FPGA leftover and is documented only
|
||||
* in Errata 4.12. It looks like that it was part of an IRQ-controller in FPGA
|
||||
* and someone forgot to remove it while switching to the core and moving to
|
||||
* CESA_SA_INT_STATUS.
|
||||
*/
|
||||
#define CESA_SA_FPGA_INT_STATUS 0xdd68
|
||||
#define CESA_SA_INT_STATUS 0xde20
|
||||
#define CESA_SA_INT_AUTH_DONE BIT(0)
|
||||
#define CESA_SA_INT_DES_E_DONE BIT(1)
|
||||
#define CESA_SA_INT_AES_E_DONE BIT(2)
|
||||
#define CESA_SA_INT_AES_D_DONE BIT(3)
|
||||
#define CESA_SA_INT_ENC_DONE BIT(4)
|
||||
#define CESA_SA_INT_ACCEL0_DONE BIT(5)
|
||||
#define CESA_SA_INT_ACCEL1_DONE BIT(6)
|
||||
#define CESA_SA_INT_ACC0_IDMA_DONE BIT(7)
|
||||
#define CESA_SA_INT_ACC1_IDMA_DONE BIT(8)
|
||||
#define CESA_SA_INT_IDMA_DONE BIT(9)
|
||||
#define CESA_SA_INT_IDMA_OWN_ERR BIT(10)
|
||||
|
||||
#define CESA_SA_INT_MSK 0xde24
|
||||
|
||||
#define CESA_SA_DESC_CFG_OP_MAC_ONLY 0
|
||||
#define CESA_SA_DESC_CFG_OP_CRYPT_ONLY 1
|
||||
#define CESA_SA_DESC_CFG_OP_MAC_CRYPT 2
|
||||
#define CESA_SA_DESC_CFG_OP_CRYPT_MAC 3
|
||||
#define CESA_SA_DESC_CFG_OP_MSK GENMASK(1, 0)
|
||||
#define CESA_SA_DESC_CFG_MACM_SHA256 (1 << 4)
|
||||
#define CESA_SA_DESC_CFG_MACM_HMAC_SHA256 (3 << 4)
|
||||
#define CESA_SA_DESC_CFG_MACM_MD5 (4 << 4)
|
||||
#define CESA_SA_DESC_CFG_MACM_SHA1 (5 << 4)
|
||||
#define CESA_SA_DESC_CFG_MACM_HMAC_MD5 (6 << 4)
|
||||
#define CESA_SA_DESC_CFG_MACM_HMAC_SHA1 (7 << 4)
|
||||
#define CESA_SA_DESC_CFG_MACM_MSK GENMASK(6, 4)
|
||||
#define CESA_SA_DESC_CFG_CRYPTM_DES (1 << 8)
|
||||
#define CESA_SA_DESC_CFG_CRYPTM_3DES (2 << 8)
|
||||
#define CESA_SA_DESC_CFG_CRYPTM_AES (3 << 8)
|
||||
#define CESA_SA_DESC_CFG_CRYPTM_MSK GENMASK(9, 8)
|
||||
#define CESA_SA_DESC_CFG_DIR_ENC (0 << 12)
|
||||
#define CESA_SA_DESC_CFG_DIR_DEC (1 << 12)
|
||||
#define CESA_SA_DESC_CFG_CRYPTCM_ECB (0 << 16)
|
||||
#define CESA_SA_DESC_CFG_CRYPTCM_CBC (1 << 16)
|
||||
#define CESA_SA_DESC_CFG_CRYPTCM_MSK BIT(16)
|
||||
#define CESA_SA_DESC_CFG_3DES_EEE (0 << 20)
|
||||
#define CESA_SA_DESC_CFG_3DES_EDE (1 << 20)
|
||||
#define CESA_SA_DESC_CFG_AES_LEN_128 (0 << 24)
|
||||
#define CESA_SA_DESC_CFG_AES_LEN_192 (1 << 24)
|
||||
#define CESA_SA_DESC_CFG_AES_LEN_256 (2 << 24)
|
||||
#define CESA_SA_DESC_CFG_AES_LEN_MSK GENMASK(25, 24)
|
||||
#define CESA_SA_DESC_CFG_NOT_FRAG (0 << 30)
|
||||
#define CESA_SA_DESC_CFG_FIRST_FRAG (1 << 30)
|
||||
#define CESA_SA_DESC_CFG_LAST_FRAG (2 << 30)
|
||||
#define CESA_SA_DESC_CFG_MID_FRAG (3 << 30)
|
||||
#define CESA_SA_DESC_CFG_FRAG_MSK GENMASK(31, 30)
|
||||
|
||||
/*
|
||||
* /-----------\ 0
|
||||
* | ACCEL CFG | 4 * 8
|
||||
* |-----------| 0x20
|
||||
* | CRYPT KEY | 8 * 4
|
||||
* |-----------| 0x40
|
||||
* | IV IN | 4 * 4
|
||||
* |-----------| 0x40 (inplace)
|
||||
* | IV BUF | 4 * 4
|
||||
* |-----------| 0x80
|
||||
* | DATA IN | 16 * x (max ->max_req_size)
|
||||
* |-----------| 0x80 (inplace operation)
|
||||
* | DATA OUT | 16 * x (max ->max_req_size)
|
||||
* \-----------/ SRAM size
|
||||
*/
|
||||
|
||||
/*
|
||||
* Hashing memory map:
|
||||
* /-----------\ 0
|
||||
* | ACCEL CFG | 4 * 8
|
||||
* |-----------| 0x20
|
||||
* | Inner IV | 8 * 4
|
||||
* |-----------| 0x40
|
||||
* | Outer IV | 8 * 4
|
||||
* |-----------| 0x60
|
||||
* | Output BUF| 8 * 4
|
||||
* |-----------| 0x80
|
||||
* | DATA IN | 64 * x (max ->max_req_size)
|
||||
* \-----------/ SRAM size
|
||||
*/
|
||||
|
||||
#define CESA_SA_CFG_SRAM_OFFSET 0x00
|
||||
#define CESA_SA_DATA_SRAM_OFFSET 0x80
|
||||
|
||||
#define CESA_SA_CRYPT_KEY_SRAM_OFFSET 0x20
|
||||
#define CESA_SA_CRYPT_IV_SRAM_OFFSET 0x40
|
||||
|
||||
#define CESA_SA_MAC_IIV_SRAM_OFFSET 0x20
|
||||
#define CESA_SA_MAC_OIV_SRAM_OFFSET 0x40
|
||||
#define CESA_SA_MAC_DIG_SRAM_OFFSET 0x60
|
||||
|
||||
#define CESA_SA_DESC_CRYPT_DATA(offset) \
|
||||
cpu_to_le32((CESA_SA_DATA_SRAM_OFFSET + (offset)) | \
|
||||
((CESA_SA_DATA_SRAM_OFFSET + (offset)) << 16))
|
||||
|
||||
#define CESA_SA_DESC_CRYPT_IV(offset) \
|
||||
cpu_to_le32((CESA_SA_CRYPT_IV_SRAM_OFFSET + (offset)) | \
|
||||
((CESA_SA_CRYPT_IV_SRAM_OFFSET + (offset)) << 16))
|
||||
|
||||
#define CESA_SA_DESC_CRYPT_KEY(offset) \
|
||||
cpu_to_le32(CESA_SA_CRYPT_KEY_SRAM_OFFSET + (offset))
|
||||
|
||||
#define CESA_SA_DESC_MAC_DATA(offset) \
|
||||
cpu_to_le32(CESA_SA_DATA_SRAM_OFFSET + (offset))
|
||||
#define CESA_SA_DESC_MAC_DATA_MSK GENMASK(15, 0)
|
||||
|
||||
#define CESA_SA_DESC_MAC_TOTAL_LEN(total_len) cpu_to_le32((total_len) << 16)
|
||||
#define CESA_SA_DESC_MAC_TOTAL_LEN_MSK GENMASK(31, 16)
|
||||
|
||||
#define CESA_SA_DESC_MAC_SRC_TOTAL_LEN_MAX 0xffff
|
||||
|
||||
#define CESA_SA_DESC_MAC_DIGEST(offset) \
|
||||
cpu_to_le32(CESA_SA_MAC_DIG_SRAM_OFFSET + (offset))
|
||||
#define CESA_SA_DESC_MAC_DIGEST_MSK GENMASK(15, 0)
|
||||
|
||||
#define CESA_SA_DESC_MAC_FRAG_LEN(frag_len) cpu_to_le32((frag_len) << 16)
|
||||
#define CESA_SA_DESC_MAC_FRAG_LEN_MSK GENMASK(31, 16)
|
||||
|
||||
#define CESA_SA_DESC_MAC_IV(offset) \
|
||||
cpu_to_le32((CESA_SA_MAC_IIV_SRAM_OFFSET + (offset)) | \
|
||||
((CESA_SA_MAC_OIV_SRAM_OFFSET + (offset)) << 16))
|
||||
|
||||
#define CESA_SA_SRAM_SIZE 2048
|
||||
#define CESA_SA_SRAM_PAYLOAD_SIZE (cesa_dev->sram_size - \
|
||||
CESA_SA_DATA_SRAM_OFFSET)
|
||||
|
||||
#define CESA_SA_DEFAULT_SRAM_SIZE 2048
|
||||
#define CESA_SA_MIN_SRAM_SIZE 1024
|
||||
|
||||
#define CESA_SA_SRAM_MSK (2048 - 1)
|
||||
|
||||
#define CESA_MAX_HASH_BLOCK_SIZE 64
|
||||
#define CESA_HASH_BLOCK_SIZE_MSK (CESA_MAX_HASH_BLOCK_SIZE - 1)
|
||||
|
||||
/**
|
||||
* struct mv_cesa_sec_accel_desc - security accelerator descriptor
|
||||
* @config: engine config
|
||||
* @enc_p: input and output data pointers for a cipher operation
|
||||
* @enc_len: cipher operation length
|
||||
* @enc_key_p: cipher key pointer
|
||||
* @enc_iv: cipher IV pointers
|
||||
* @mac_src_p: input pointer and total hash length
|
||||
* @mac_digest: digest pointer and hash operation length
|
||||
* @mac_iv: hmac IV pointers
|
||||
*
|
||||
* Structure passed to the CESA engine to describe the crypto operation
|
||||
* to be executed.
|
||||
*/
|
||||
struct mv_cesa_sec_accel_desc {
|
||||
u32 config;
|
||||
u32 enc_p;
|
||||
u32 enc_len;
|
||||
u32 enc_key_p;
|
||||
u32 enc_iv;
|
||||
u32 mac_src_p;
|
||||
u32 mac_digest;
|
||||
u32 mac_iv;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mv_cesa_blkcipher_op_ctx - cipher operation context
|
||||
* @key: cipher key
|
||||
* @iv: cipher IV
|
||||
*
|
||||
* Context associated to a cipher operation.
|
||||
*/
|
||||
struct mv_cesa_blkcipher_op_ctx {
|
||||
u32 key[8];
|
||||
u32 iv[4];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mv_cesa_hash_op_ctx - hash or hmac operation context
|
||||
* @key: cipher key
|
||||
* @iv: cipher IV
|
||||
*
|
||||
* Context associated to an hash or hmac operation.
|
||||
*/
|
||||
struct mv_cesa_hash_op_ctx {
|
||||
u32 iv[16];
|
||||
u32 hash[8];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mv_cesa_op_ctx - crypto operation context
|
||||
* @desc: CESA descriptor
|
||||
* @ctx: context associated to the crypto operation
|
||||
*
|
||||
* Context associated to a crypto operation.
|
||||
*/
|
||||
struct mv_cesa_op_ctx {
|
||||
struct mv_cesa_sec_accel_desc desc;
|
||||
union {
|
||||
struct mv_cesa_blkcipher_op_ctx blkcipher;
|
||||
struct mv_cesa_hash_op_ctx hash;
|
||||
} ctx;
|
||||
};
|
||||
|
||||
/* TDMA descriptor flags */
|
||||
#define CESA_TDMA_DST_IN_SRAM BIT(31)
|
||||
#define CESA_TDMA_SRC_IN_SRAM BIT(30)
|
||||
#define CESA_TDMA_TYPE_MSK GENMASK(29, 0)
|
||||
#define CESA_TDMA_DUMMY 0
|
||||
#define CESA_TDMA_DATA 1
|
||||
#define CESA_TDMA_OP 2
|
||||
|
||||
/**
|
||||
* struct mv_cesa_tdma_desc - TDMA descriptor
|
||||
* @byte_cnt: number of bytes to transfer
|
||||
* @src: DMA address of the source
|
||||
* @dst: DMA address of the destination
|
||||
* @next_dma: DMA address of the next TDMA descriptor
|
||||
* @cur_dma: DMA address of this TDMA descriptor
|
||||
* @next: pointer to the next TDMA descriptor
|
||||
* @op: CESA operation attached to this TDMA descriptor
|
||||
* @data: raw data attached to this TDMA descriptor
|
||||
* @flags: flags describing the TDMA transfer. See the
|
||||
* "TDMA descriptor flags" section above
|
||||
*
|
||||
* TDMA descriptor used to create a transfer chain describing a crypto
|
||||
* operation.
|
||||
*/
|
||||
struct mv_cesa_tdma_desc {
|
||||
u32 byte_cnt;
|
||||
u32 src;
|
||||
u32 dst;
|
||||
u32 next_dma;
|
||||
u32 cur_dma;
|
||||
struct mv_cesa_tdma_desc *next;
|
||||
union {
|
||||
struct mv_cesa_op_ctx *op;
|
||||
void *data;
|
||||
};
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mv_cesa_sg_dma_iter - scatter-gather iterator
|
||||
* @dir: transfer direction
|
||||
* @sg: scatter list
|
||||
* @offset: current position in the scatter list
|
||||
* @op_offset: current position in the crypto operation
|
||||
*
|
||||
* Iterator used to iterate over a scatterlist while creating a TDMA chain for
|
||||
* a crypto operation.
|
||||
*/
|
||||
struct mv_cesa_sg_dma_iter {
|
||||
enum dma_data_direction dir;
|
||||
struct scatterlist *sg;
|
||||
unsigned int offset;
|
||||
unsigned int op_offset;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mv_cesa_dma_iter - crypto operation iterator
|
||||
* @len: the crypto operation length
|
||||
* @offset: current position in the crypto operation
|
||||
* @op_len: sub-operation length (the crypto engine can only act on 2kb
|
||||
* chunks)
|
||||
*
|
||||
* Iterator used to create a TDMA chain for a given crypto operation.
|
||||
*/
|
||||
struct mv_cesa_dma_iter {
|
||||
unsigned int len;
|
||||
unsigned int offset;
|
||||
unsigned int op_len;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mv_cesa_tdma_chain - TDMA chain
|
||||
* @first: first entry in the TDMA chain
|
||||
* @last: last entry in the TDMA chain
|
||||
*
|
||||
* Stores a TDMA chain for a specific crypto operation.
|
||||
*/
|
||||
struct mv_cesa_tdma_chain {
|
||||
struct mv_cesa_tdma_desc *first;
|
||||
struct mv_cesa_tdma_desc *last;
|
||||
};
|
||||
|
||||
struct mv_cesa_engine;
|
||||
|
||||
/**
|
||||
* struct mv_cesa_caps - CESA device capabilities
|
||||
* @engines: number of engines
|
||||
* @has_tdma: whether this device has a TDMA block
|
||||
* @cipher_algs: supported cipher algorithms
|
||||
* @ncipher_algs: number of supported cipher algorithms
|
||||
* @ahash_algs: supported hash algorithms
|
||||
* @nahash_algs: number of supported hash algorithms
|
||||
*
|
||||
* Structure used to describe CESA device capabilities.
|
||||
*/
|
||||
struct mv_cesa_caps {
|
||||
int nengines;
|
||||
bool has_tdma;
|
||||
struct crypto_alg **cipher_algs;
|
||||
int ncipher_algs;
|
||||
struct ahash_alg **ahash_algs;
|
||||
int nahash_algs;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mv_cesa_dev_dma - DMA pools
|
||||
* @tdma_desc_pool: TDMA desc pool
|
||||
* @op_pool: crypto operation pool
|
||||
* @cache_pool: data cache pool (used by hash implementation when the
|
||||
* hash request is smaller than the hash block size)
|
||||
* @padding_pool: padding pool (used by hash implementation when hardware
|
||||
* padding cannot be used)
|
||||
*
|
||||
* Structure containing the different DMA pools used by this driver.
|
||||
*/
|
||||
struct mv_cesa_dev_dma {
|
||||
struct dma_pool *tdma_desc_pool;
|
||||
struct dma_pool *op_pool;
|
||||
struct dma_pool *cache_pool;
|
||||
struct dma_pool *padding_pool;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mv_cesa_dev - CESA device
|
||||
* @caps: device capabilities
|
||||
* @regs: device registers
|
||||
* @sram_size: usable SRAM size
|
||||
* @lock: device lock
|
||||
* @queue: crypto request queue
|
||||
* @engines: array of engines
|
||||
* @dma: dma pools
|
||||
*
|
||||
* Structure storing CESA device information.
|
||||
*/
|
||||
struct mv_cesa_dev {
|
||||
const struct mv_cesa_caps *caps;
|
||||
void __iomem *regs;
|
||||
struct device *dev;
|
||||
unsigned int sram_size;
|
||||
spinlock_t lock;
|
||||
struct crypto_queue queue;
|
||||
struct mv_cesa_engine *engines;
|
||||
struct mv_cesa_dev_dma *dma;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mv_cesa_engine - CESA engine
|
||||
* @id: engine id
|
||||
* @regs: engine registers
|
||||
* @sram: SRAM memory region
|
||||
* @sram_dma: DMA address of the SRAM memory region
|
||||
* @lock: engine lock
|
||||
* @req: current crypto request
|
||||
* @clk: engine clk
|
||||
* @zclk: engine zclk
|
||||
* @max_req_len: maximum chunk length (useful to create the TDMA chain)
|
||||
* @int_mask: interrupt mask cache
|
||||
* @pool: memory pool pointing to the memory region reserved in
|
||||
* SRAM
|
||||
*
|
||||
* Structure storing CESA engine information.
|
||||
*/
|
||||
struct mv_cesa_engine {
|
||||
int id;
|
||||
void __iomem *regs;
|
||||
void __iomem *sram;
|
||||
dma_addr_t sram_dma;
|
||||
spinlock_t lock;
|
||||
struct crypto_async_request *req;
|
||||
struct clk *clk;
|
||||
struct clk *zclk;
|
||||
size_t max_req_len;
|
||||
u32 int_mask;
|
||||
struct gen_pool *pool;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mv_cesa_req_ops - CESA request operations
|
||||
* @prepare: prepare a request to be executed on the specified engine
|
||||
* @process: process a request chunk result (should return 0 if the
|
||||
* operation, -EINPROGRESS if it needs more steps or an error
|
||||
* code)
|
||||
* @step: launch the crypto operation on the next chunk
|
||||
* @cleanup: cleanup the crypto request (release associated data)
|
||||
*/
|
||||
struct mv_cesa_req_ops {
|
||||
void (*prepare)(struct crypto_async_request *req,
|
||||
struct mv_cesa_engine *engine);
|
||||
int (*process)(struct crypto_async_request *req, u32 status);
|
||||
void (*step)(struct crypto_async_request *req);
|
||||
void (*cleanup)(struct crypto_async_request *req);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mv_cesa_ctx - CESA operation context
|
||||
* @ops: crypto operations
|
||||
*
|
||||
* Base context structure inherited by operation specific ones.
|
||||
*/
|
||||
struct mv_cesa_ctx {
|
||||
const struct mv_cesa_req_ops *ops;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mv_cesa_hash_ctx - CESA hash operation context
|
||||
* @base: base context structure
|
||||
*
|
||||
* Hash context structure.
|
||||
*/
|
||||
struct mv_cesa_hash_ctx {
|
||||
struct mv_cesa_ctx base;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mv_cesa_hash_ctx - CESA hmac operation context
|
||||
* @base: base context structure
|
||||
* @iv: initialization vectors
|
||||
*
|
||||
* HMAC context structure.
|
||||
*/
|
||||
struct mv_cesa_hmac_ctx {
|
||||
struct mv_cesa_ctx base;
|
||||
u32 iv[16];
|
||||
};
|
||||
|
||||
/**
|
||||
* enum mv_cesa_req_type - request type definitions
|
||||
* @CESA_STD_REQ: standard request
|
||||
* @CESA_DMA_REQ: DMA request
|
||||
*/
|
||||
enum mv_cesa_req_type {
|
||||
CESA_STD_REQ,
|
||||
CESA_DMA_REQ,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mv_cesa_req - CESA request
|
||||
* @type: request type
|
||||
* @engine: engine associated with this request
|
||||
*/
|
||||
struct mv_cesa_req {
|
||||
enum mv_cesa_req_type type;
|
||||
struct mv_cesa_engine *engine;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mv_cesa_tdma_req - CESA TDMA request
|
||||
* @base: base information
|
||||
* @chain: TDMA chain
|
||||
*/
|
||||
struct mv_cesa_tdma_req {
|
||||
struct mv_cesa_req base;
|
||||
struct mv_cesa_tdma_chain chain;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mv_cesa_sg_std_iter - CESA scatter-gather iterator for standard
|
||||
* requests
|
||||
* @iter: sg mapping iterator
|
||||
* @offset: current offset in the SG entry mapped in memory
|
||||
*/
|
||||
struct mv_cesa_sg_std_iter {
|
||||
struct sg_mapping_iter iter;
|
||||
unsigned int offset;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mv_cesa_ablkcipher_std_req - cipher standard request
|
||||
* @base: base information
|
||||
* @op: operation context
|
||||
* @offset: current operation offset
|
||||
* @size: size of the crypto operation
|
||||
*/
|
||||
struct mv_cesa_ablkcipher_std_req {
|
||||
struct mv_cesa_req base;
|
||||
struct mv_cesa_op_ctx op;
|
||||
unsigned int offset;
|
||||
unsigned int size;
|
||||
bool skip_ctx;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mv_cesa_ablkcipher_req - cipher request
|
||||
* @req: type specific request information
|
||||
* @src_nents: number of entries in the src sg list
|
||||
* @dst_nents: number of entries in the dest sg list
|
||||
*/
|
||||
struct mv_cesa_ablkcipher_req {
|
||||
union {
|
||||
struct mv_cesa_req base;
|
||||
struct mv_cesa_tdma_req dma;
|
||||
struct mv_cesa_ablkcipher_std_req std;
|
||||
} req;
|
||||
int src_nents;
|
||||
int dst_nents;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mv_cesa_ahash_std_req - standard hash request
|
||||
* @base: base information
|
||||
* @offset: current operation offset
|
||||
*/
|
||||
struct mv_cesa_ahash_std_req {
|
||||
struct mv_cesa_req base;
|
||||
unsigned int offset;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mv_cesa_ahash_dma_req - DMA hash request
|
||||
* @base: base information
|
||||
* @padding: padding buffer
|
||||
* @padding_dma: DMA address of the padding buffer
|
||||
* @cache_dma: DMA address of the cache buffer
|
||||
*/
|
||||
struct mv_cesa_ahash_dma_req {
|
||||
struct mv_cesa_tdma_req base;
|
||||
u8 *padding;
|
||||
dma_addr_t padding_dma;
|
||||
dma_addr_t cache_dma;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mv_cesa_ahash_req - hash request
|
||||
* @req: type specific request information
|
||||
* @cache: cache buffer
|
||||
* @cache_ptr: write pointer in the cache buffer
|
||||
* @len: hash total length
|
||||
* @src_nents: number of entries in the scatterlist
|
||||
* @last_req: define whether the current operation is the last one
|
||||
* or not
|
||||
* @state: hash state
|
||||
*/
|
||||
struct mv_cesa_ahash_req {
|
||||
union {
|
||||
struct mv_cesa_req base;
|
||||
struct mv_cesa_ahash_dma_req dma;
|
||||
struct mv_cesa_ahash_std_req std;
|
||||
} req;
|
||||
struct mv_cesa_op_ctx op_tmpl;
|
||||
u8 *cache;
|
||||
unsigned int cache_ptr;
|
||||
u64 len;
|
||||
int src_nents;
|
||||
bool last_req;
|
||||
__be32 state[8];
|
||||
};
|
||||
|
||||
/* CESA functions */
|
||||
|
||||
extern struct mv_cesa_dev *cesa_dev;
|
||||
|
||||
static inline void mv_cesa_update_op_cfg(struct mv_cesa_op_ctx *op,
|
||||
u32 cfg, u32 mask)
|
||||
{
|
||||
op->desc.config &= cpu_to_le32(~mask);
|
||||
op->desc.config |= cpu_to_le32(cfg);
|
||||
}
|
||||
|
||||
static inline u32 mv_cesa_get_op_cfg(struct mv_cesa_op_ctx *op)
|
||||
{
|
||||
return le32_to_cpu(op->desc.config);
|
||||
}
|
||||
|
||||
static inline void mv_cesa_set_op_cfg(struct mv_cesa_op_ctx *op, u32 cfg)
|
||||
{
|
||||
op->desc.config = cpu_to_le32(cfg);
|
||||
}
|
||||
|
||||
static inline void mv_cesa_adjust_op(struct mv_cesa_engine *engine,
|
||||
struct mv_cesa_op_ctx *op)
|
||||
{
|
||||
u32 offset = engine->sram_dma & CESA_SA_SRAM_MSK;
|
||||
|
||||
op->desc.enc_p = CESA_SA_DESC_CRYPT_DATA(offset);
|
||||
op->desc.enc_key_p = CESA_SA_DESC_CRYPT_KEY(offset);
|
||||
op->desc.enc_iv = CESA_SA_DESC_CRYPT_IV(offset);
|
||||
op->desc.mac_src_p &= ~CESA_SA_DESC_MAC_DATA_MSK;
|
||||
op->desc.mac_src_p |= CESA_SA_DESC_MAC_DATA(offset);
|
||||
op->desc.mac_digest &= ~CESA_SA_DESC_MAC_DIGEST_MSK;
|
||||
op->desc.mac_digest |= CESA_SA_DESC_MAC_DIGEST(offset);
|
||||
op->desc.mac_iv = CESA_SA_DESC_MAC_IV(offset);
|
||||
}
|
||||
|
||||
static inline void mv_cesa_set_crypt_op_len(struct mv_cesa_op_ctx *op, int len)
|
||||
{
|
||||
op->desc.enc_len = cpu_to_le32(len);
|
||||
}
|
||||
|
||||
static inline void mv_cesa_set_mac_op_total_len(struct mv_cesa_op_ctx *op,
|
||||
int len)
|
||||
{
|
||||
op->desc.mac_src_p &= ~CESA_SA_DESC_MAC_TOTAL_LEN_MSK;
|
||||
op->desc.mac_src_p |= CESA_SA_DESC_MAC_TOTAL_LEN(len);
|
||||
}
|
||||
|
||||
static inline void mv_cesa_set_mac_op_frag_len(struct mv_cesa_op_ctx *op,
|
||||
int len)
|
||||
{
|
||||
op->desc.mac_digest &= ~CESA_SA_DESC_MAC_FRAG_LEN_MSK;
|
||||
op->desc.mac_digest |= CESA_SA_DESC_MAC_FRAG_LEN(len);
|
||||
}
|
||||
|
||||
static inline void mv_cesa_set_int_mask(struct mv_cesa_engine *engine,
|
||||
u32 int_mask)
|
||||
{
|
||||
if (int_mask == engine->int_mask)
|
||||
return;
|
||||
|
||||
writel(int_mask, engine->regs + CESA_SA_INT_MSK);
|
||||
engine->int_mask = int_mask;
|
||||
}
|
||||
|
||||
static inline u32 mv_cesa_get_int_mask(struct mv_cesa_engine *engine)
|
||||
{
|
||||
return engine->int_mask;
|
||||
}
|
||||
|
||||
int mv_cesa_queue_req(struct crypto_async_request *req);
|
||||
|
||||
/* TDMA functions */
|
||||
|
||||
static inline void mv_cesa_req_dma_iter_init(struct mv_cesa_dma_iter *iter,
|
||||
unsigned int len)
|
||||
{
|
||||
iter->len = len;
|
||||
iter->op_len = min(len, CESA_SA_SRAM_PAYLOAD_SIZE);
|
||||
iter->offset = 0;
|
||||
}
|
||||
|
||||
static inline void mv_cesa_sg_dma_iter_init(struct mv_cesa_sg_dma_iter *iter,
|
||||
struct scatterlist *sg,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
iter->op_offset = 0;
|
||||
iter->offset = 0;
|
||||
iter->sg = sg;
|
||||
iter->dir = dir;
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
mv_cesa_req_dma_iter_transfer_len(struct mv_cesa_dma_iter *iter,
|
||||
struct mv_cesa_sg_dma_iter *sgiter)
|
||||
{
|
||||
return min(iter->op_len - sgiter->op_offset,
|
||||
sg_dma_len(sgiter->sg) - sgiter->offset);
|
||||
}
|
||||
|
||||
bool mv_cesa_req_dma_iter_next_transfer(struct mv_cesa_dma_iter *chain,
|
||||
struct mv_cesa_sg_dma_iter *sgiter,
|
||||
unsigned int len);
|
||||
|
||||
static inline bool mv_cesa_req_dma_iter_next_op(struct mv_cesa_dma_iter *iter)
|
||||
{
|
||||
iter->offset += iter->op_len;
|
||||
iter->op_len = min(iter->len - iter->offset,
|
||||
CESA_SA_SRAM_PAYLOAD_SIZE);
|
||||
|
||||
return iter->op_len;
|
||||
}
|
||||
|
||||
void mv_cesa_dma_step(struct mv_cesa_tdma_req *dreq);
|
||||
|
||||
static inline int mv_cesa_dma_process(struct mv_cesa_tdma_req *dreq,
|
||||
u32 status)
|
||||
{
|
||||
if (!(status & CESA_SA_INT_ACC0_IDMA_DONE))
|
||||
return -EINPROGRESS;
|
||||
|
||||
if (status & CESA_SA_INT_IDMA_OWN_ERR)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mv_cesa_dma_prepare(struct mv_cesa_tdma_req *dreq,
|
||||
struct mv_cesa_engine *engine);
|
||||
|
||||
void mv_cesa_dma_cleanup(struct mv_cesa_tdma_req *dreq);
|
||||
|
||||
static inline void
|
||||
mv_cesa_tdma_desc_iter_init(struct mv_cesa_tdma_chain *chain)
|
||||
{
|
||||
memset(chain, 0, sizeof(*chain));
|
||||
}
|
||||
|
||||
struct mv_cesa_op_ctx *mv_cesa_dma_add_op(struct mv_cesa_tdma_chain *chain,
|
||||
const struct mv_cesa_op_ctx *op_templ,
|
||||
bool skip_ctx,
|
||||
gfp_t flags);
|
||||
|
||||
int mv_cesa_dma_add_data_transfer(struct mv_cesa_tdma_chain *chain,
|
||||
dma_addr_t dst, dma_addr_t src, u32 size,
|
||||
u32 flags, gfp_t gfp_flags);
|
||||
|
||||
int mv_cesa_dma_add_dummy_launch(struct mv_cesa_tdma_chain *chain,
|
||||
u32 flags);
|
||||
|
||||
int mv_cesa_dma_add_dummy_end(struct mv_cesa_tdma_chain *chain, u32 flags);
|
||||
|
||||
int mv_cesa_dma_add_op_transfers(struct mv_cesa_tdma_chain *chain,
|
||||
struct mv_cesa_dma_iter *dma_iter,
|
||||
struct mv_cesa_sg_dma_iter *sgiter,
|
||||
gfp_t gfp_flags);
|
||||
|
||||
/* Algorithm definitions */
|
||||
|
||||
extern struct ahash_alg mv_md5_alg;
|
||||
extern struct ahash_alg mv_sha1_alg;
|
||||
extern struct ahash_alg mv_sha256_alg;
|
||||
extern struct ahash_alg mv_ahmac_md5_alg;
|
||||
extern struct ahash_alg mv_ahmac_sha1_alg;
|
||||
extern struct ahash_alg mv_ahmac_sha256_alg;
|
||||
|
||||
extern struct crypto_alg mv_cesa_ecb_des_alg;
|
||||
extern struct crypto_alg mv_cesa_cbc_des_alg;
|
||||
extern struct crypto_alg mv_cesa_ecb_des3_ede_alg;
|
||||
extern struct crypto_alg mv_cesa_cbc_des3_ede_alg;
|
||||
extern struct crypto_alg mv_cesa_ecb_aes_alg;
|
||||
extern struct crypto_alg mv_cesa_cbc_aes_alg;
|
||||
|
||||
#endif /* __MARVELL_CESA_H__ */
|
797
drivers/crypto/marvell/cipher.c
Normal file
797
drivers/crypto/marvell/cipher.c
Normal file
@ -0,0 +1,797 @@
|
||||
/*
|
||||
* Cipher algorithms supported by the CESA: DES, 3DES and AES.
|
||||
*
|
||||
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
|
||||
* Author: Arnaud Ebalard <arno@natisbad.org>
|
||||
*
|
||||
* This work is based on an initial version written by
|
||||
* Sebastian Andrzej Siewior < sebastian at breakpoint dot cc >
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <crypto/aes.h>
|
||||
#include <crypto/des.h>
|
||||
|
||||
#include "cesa.h"
|
||||
|
||||
struct mv_cesa_des_ctx {
|
||||
struct mv_cesa_ctx base;
|
||||
u8 key[DES_KEY_SIZE];
|
||||
};
|
||||
|
||||
struct mv_cesa_des3_ctx {
|
||||
struct mv_cesa_ctx base;
|
||||
u8 key[DES3_EDE_KEY_SIZE];
|
||||
};
|
||||
|
||||
struct mv_cesa_aes_ctx {
|
||||
struct mv_cesa_ctx base;
|
||||
struct crypto_aes_ctx aes;
|
||||
};
|
||||
|
||||
struct mv_cesa_ablkcipher_dma_iter {
|
||||
struct mv_cesa_dma_iter base;
|
||||
struct mv_cesa_sg_dma_iter src;
|
||||
struct mv_cesa_sg_dma_iter dst;
|
||||
};
|
||||
|
||||
static inline void
|
||||
mv_cesa_ablkcipher_req_iter_init(struct mv_cesa_ablkcipher_dma_iter *iter,
|
||||
struct ablkcipher_request *req)
|
||||
{
|
||||
mv_cesa_req_dma_iter_init(&iter->base, req->nbytes);
|
||||
mv_cesa_sg_dma_iter_init(&iter->src, req->src, DMA_TO_DEVICE);
|
||||
mv_cesa_sg_dma_iter_init(&iter->dst, req->dst, DMA_FROM_DEVICE);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
mv_cesa_ablkcipher_req_iter_next_op(struct mv_cesa_ablkcipher_dma_iter *iter)
|
||||
{
|
||||
iter->src.op_offset = 0;
|
||||
iter->dst.op_offset = 0;
|
||||
|
||||
return mv_cesa_req_dma_iter_next_op(&iter->base);
|
||||
}
|
||||
|
||||
static inline void
|
||||
mv_cesa_ablkcipher_dma_cleanup(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
|
||||
|
||||
if (req->dst != req->src) {
|
||||
dma_unmap_sg(cesa_dev->dev, req->dst, creq->dst_nents,
|
||||
DMA_FROM_DEVICE);
|
||||
dma_unmap_sg(cesa_dev->dev, req->src, creq->src_nents,
|
||||
DMA_TO_DEVICE);
|
||||
} else {
|
||||
dma_unmap_sg(cesa_dev->dev, req->src, creq->src_nents,
|
||||
DMA_BIDIRECTIONAL);
|
||||
}
|
||||
mv_cesa_dma_cleanup(&creq->req.dma);
|
||||
}
|
||||
|
||||
static inline void mv_cesa_ablkcipher_cleanup(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
|
||||
|
||||
if (creq->req.base.type == CESA_DMA_REQ)
|
||||
mv_cesa_ablkcipher_dma_cleanup(req);
|
||||
}
|
||||
|
||||
static void mv_cesa_ablkcipher_std_step(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
|
||||
struct mv_cesa_ablkcipher_std_req *sreq = &creq->req.std;
|
||||
struct mv_cesa_engine *engine = sreq->base.engine;
|
||||
size_t len = min_t(size_t, req->nbytes - sreq->offset,
|
||||
CESA_SA_SRAM_PAYLOAD_SIZE);
|
||||
|
||||
len = sg_pcopy_to_buffer(req->src, creq->src_nents,
|
||||
engine->sram + CESA_SA_DATA_SRAM_OFFSET,
|
||||
len, sreq->offset);
|
||||
|
||||
sreq->size = len;
|
||||
mv_cesa_set_crypt_op_len(&sreq->op, len);
|
||||
|
||||
/* FIXME: only update enc_len field */
|
||||
if (!sreq->skip_ctx) {
|
||||
memcpy(engine->sram, &sreq->op, sizeof(sreq->op));
|
||||
sreq->skip_ctx = true;
|
||||
} else {
|
||||
memcpy(engine->sram, &sreq->op, sizeof(sreq->op.desc));
|
||||
}
|
||||
|
||||
mv_cesa_set_int_mask(engine, CESA_SA_INT_ACCEL0_DONE);
|
||||
writel(CESA_SA_CFG_PARA_DIS, engine->regs + CESA_SA_CFG);
|
||||
writel(CESA_SA_CMD_EN_CESA_SA_ACCL0, engine->regs + CESA_SA_CMD);
|
||||
}
|
||||
|
||||
static int mv_cesa_ablkcipher_std_process(struct ablkcipher_request *req,
|
||||
u32 status)
|
||||
{
|
||||
struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
|
||||
struct mv_cesa_ablkcipher_std_req *sreq = &creq->req.std;
|
||||
struct mv_cesa_engine *engine = sreq->base.engine;
|
||||
size_t len;
|
||||
|
||||
len = sg_pcopy_from_buffer(req->dst, creq->dst_nents,
|
||||
engine->sram + CESA_SA_DATA_SRAM_OFFSET,
|
||||
sreq->size, sreq->offset);
|
||||
|
||||
sreq->offset += len;
|
||||
if (sreq->offset < req->nbytes)
|
||||
return -EINPROGRESS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_cesa_ablkcipher_process(struct crypto_async_request *req,
|
||||
u32 status)
|
||||
{
|
||||
struct ablkcipher_request *ablkreq = ablkcipher_request_cast(req);
|
||||
struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(ablkreq);
|
||||
struct mv_cesa_ablkcipher_std_req *sreq = &creq->req.std;
|
||||
struct mv_cesa_engine *engine = sreq->base.engine;
|
||||
int ret;
|
||||
|
||||
if (creq->req.base.type == CESA_DMA_REQ)
|
||||
ret = mv_cesa_dma_process(&creq->req.dma, status);
|
||||
else
|
||||
ret = mv_cesa_ablkcipher_std_process(ablkreq, status);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
memcpy(ablkreq->info, engine->sram + CESA_SA_CRYPT_IV_SRAM_OFFSET,
|
||||
crypto_ablkcipher_ivsize(crypto_ablkcipher_reqtfm(ablkreq)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mv_cesa_ablkcipher_step(struct crypto_async_request *req)
|
||||
{
|
||||
struct ablkcipher_request *ablkreq = ablkcipher_request_cast(req);
|
||||
struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(ablkreq);
|
||||
|
||||
if (creq->req.base.type == CESA_DMA_REQ)
|
||||
mv_cesa_dma_step(&creq->req.dma);
|
||||
else
|
||||
mv_cesa_ablkcipher_std_step(ablkreq);
|
||||
}
|
||||
|
||||
static inline void
|
||||
mv_cesa_ablkcipher_dma_prepare(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
|
||||
struct mv_cesa_tdma_req *dreq = &creq->req.dma;
|
||||
|
||||
mv_cesa_dma_prepare(dreq, dreq->base.engine);
|
||||
}
|
||||
|
||||
static inline void
|
||||
mv_cesa_ablkcipher_std_prepare(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
|
||||
struct mv_cesa_ablkcipher_std_req *sreq = &creq->req.std;
|
||||
struct mv_cesa_engine *engine = sreq->base.engine;
|
||||
|
||||
sreq->size = 0;
|
||||
sreq->offset = 0;
|
||||
mv_cesa_adjust_op(engine, &sreq->op);
|
||||
memcpy(engine->sram, &sreq->op, sizeof(sreq->op));
|
||||
}
|
||||
|
||||
static inline void mv_cesa_ablkcipher_prepare(struct crypto_async_request *req,
|
||||
struct mv_cesa_engine *engine)
|
||||
{
|
||||
struct ablkcipher_request *ablkreq = ablkcipher_request_cast(req);
|
||||
struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(ablkreq);
|
||||
|
||||
creq->req.base.engine = engine;
|
||||
|
||||
if (creq->req.base.type == CESA_DMA_REQ)
|
||||
mv_cesa_ablkcipher_dma_prepare(ablkreq);
|
||||
else
|
||||
mv_cesa_ablkcipher_std_prepare(ablkreq);
|
||||
}
|
||||
|
||||
static inline void
|
||||
mv_cesa_ablkcipher_req_cleanup(struct crypto_async_request *req)
|
||||
{
|
||||
struct ablkcipher_request *ablkreq = ablkcipher_request_cast(req);
|
||||
|
||||
mv_cesa_ablkcipher_cleanup(ablkreq);
|
||||
}
|
||||
|
||||
static const struct mv_cesa_req_ops mv_cesa_ablkcipher_req_ops = {
|
||||
.step = mv_cesa_ablkcipher_step,
|
||||
.process = mv_cesa_ablkcipher_process,
|
||||
.prepare = mv_cesa_ablkcipher_prepare,
|
||||
.cleanup = mv_cesa_ablkcipher_req_cleanup,
|
||||
};
|
||||
|
||||
static int mv_cesa_ablkcipher_cra_init(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct mv_cesa_aes_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
|
||||
ctx->base.ops = &mv_cesa_ablkcipher_req_ops;
|
||||
|
||||
tfm->crt_ablkcipher.reqsize = sizeof(struct mv_cesa_ablkcipher_req);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_cesa_aes_setkey(struct crypto_ablkcipher *cipher, const u8 *key,
|
||||
unsigned int len)
|
||||
{
|
||||
struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
|
||||
struct mv_cesa_aes_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
int remaining;
|
||||
int offset;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = crypto_aes_expand_key(&ctx->aes, key, len);
|
||||
if (ret) {
|
||||
crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
|
||||
return ret;
|
||||
}
|
||||
|
||||
remaining = (ctx->aes.key_length - 16) / 4;
|
||||
offset = ctx->aes.key_length + 24 - remaining;
|
||||
for (i = 0; i < remaining; i++)
|
||||
ctx->aes.key_dec[4 + i] =
|
||||
cpu_to_le32(ctx->aes.key_enc[offset + i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_cesa_des_setkey(struct crypto_ablkcipher *cipher, const u8 *key,
|
||||
unsigned int len)
|
||||
{
|
||||
struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
|
||||
struct mv_cesa_des_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
u32 tmp[DES_EXPKEY_WORDS];
|
||||
int ret;
|
||||
|
||||
if (len != DES_KEY_SIZE) {
|
||||
crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = des_ekey(tmp, key);
|
||||
if (!ret && (tfm->crt_flags & CRYPTO_TFM_REQ_WEAK_KEY)) {
|
||||
tfm->crt_flags |= CRYPTO_TFM_RES_WEAK_KEY;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(ctx->key, key, DES_KEY_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_cesa_des3_ede_setkey(struct crypto_ablkcipher *cipher,
|
||||
const u8 *key, unsigned int len)
|
||||
{
|
||||
struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
|
||||
struct mv_cesa_des_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
|
||||
if (len != DES3_EDE_KEY_SIZE) {
|
||||
crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(ctx->key, key, DES3_EDE_KEY_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_cesa_ablkcipher_dma_req_init(struct ablkcipher_request *req,
|
||||
const struct mv_cesa_op_ctx *op_templ)
|
||||
{
|
||||
struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
|
||||
gfp_t flags = (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
|
||||
GFP_KERNEL : GFP_ATOMIC;
|
||||
struct mv_cesa_tdma_req *dreq = &creq->req.dma;
|
||||
struct mv_cesa_ablkcipher_dma_iter iter;
|
||||
struct mv_cesa_tdma_chain chain;
|
||||
bool skip_ctx = false;
|
||||
int ret;
|
||||
|
||||
dreq->base.type = CESA_DMA_REQ;
|
||||
dreq->chain.first = NULL;
|
||||
dreq->chain.last = NULL;
|
||||
|
||||
if (req->src != req->dst) {
|
||||
ret = dma_map_sg(cesa_dev->dev, req->src, creq->src_nents,
|
||||
DMA_TO_DEVICE);
|
||||
if (!ret)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = dma_map_sg(cesa_dev->dev, req->dst, creq->dst_nents,
|
||||
DMA_FROM_DEVICE);
|
||||
if (!ret) {
|
||||
ret = -ENOMEM;
|
||||
goto err_unmap_src;
|
||||
}
|
||||
} else {
|
||||
ret = dma_map_sg(cesa_dev->dev, req->src, creq->src_nents,
|
||||
DMA_BIDIRECTIONAL);
|
||||
if (!ret)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mv_cesa_tdma_desc_iter_init(&chain);
|
||||
mv_cesa_ablkcipher_req_iter_init(&iter, req);
|
||||
|
||||
do {
|
||||
struct mv_cesa_op_ctx *op;
|
||||
|
||||
op = mv_cesa_dma_add_op(&chain, op_templ, skip_ctx, flags);
|
||||
if (IS_ERR(op)) {
|
||||
ret = PTR_ERR(op);
|
||||
goto err_free_tdma;
|
||||
}
|
||||
skip_ctx = true;
|
||||
|
||||
mv_cesa_set_crypt_op_len(op, iter.base.op_len);
|
||||
|
||||
/* Add input transfers */
|
||||
ret = mv_cesa_dma_add_op_transfers(&chain, &iter.base,
|
||||
&iter.src, flags);
|
||||
if (ret)
|
||||
goto err_free_tdma;
|
||||
|
||||
/* Add dummy desc to launch the crypto operation */
|
||||
ret = mv_cesa_dma_add_dummy_launch(&chain, flags);
|
||||
if (ret)
|
||||
goto err_free_tdma;
|
||||
|
||||
/* Add output transfers */
|
||||
ret = mv_cesa_dma_add_op_transfers(&chain, &iter.base,
|
||||
&iter.dst, flags);
|
||||
if (ret)
|
||||
goto err_free_tdma;
|
||||
|
||||
} while (mv_cesa_ablkcipher_req_iter_next_op(&iter));
|
||||
|
||||
dreq->chain = chain;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_tdma:
|
||||
mv_cesa_dma_cleanup(dreq);
|
||||
if (req->dst != req->src)
|
||||
dma_unmap_sg(cesa_dev->dev, req->dst, creq->dst_nents,
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
err_unmap_src:
|
||||
dma_unmap_sg(cesa_dev->dev, req->src, creq->src_nents,
|
||||
req->dst != req->src ? DMA_TO_DEVICE : DMA_BIDIRECTIONAL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
mv_cesa_ablkcipher_std_req_init(struct ablkcipher_request *req,
|
||||
const struct mv_cesa_op_ctx *op_templ)
|
||||
{
|
||||
struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
|
||||
struct mv_cesa_ablkcipher_std_req *sreq = &creq->req.std;
|
||||
|
||||
sreq->base.type = CESA_STD_REQ;
|
||||
sreq->op = *op_templ;
|
||||
sreq->skip_ctx = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_cesa_ablkcipher_req_init(struct ablkcipher_request *req,
|
||||
struct mv_cesa_op_ctx *tmpl)
|
||||
{
|
||||
struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
|
||||
struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
|
||||
unsigned int blksize = crypto_ablkcipher_blocksize(tfm);
|
||||
int ret;
|
||||
|
||||
if (!IS_ALIGNED(req->nbytes, blksize))
|
||||
return -EINVAL;
|
||||
|
||||
creq->src_nents = sg_nents_for_len(req->src, req->nbytes);
|
||||
creq->dst_nents = sg_nents_for_len(req->dst, req->nbytes);
|
||||
|
||||
mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_OP_CRYPT_ONLY,
|
||||
CESA_SA_DESC_CFG_OP_MSK);
|
||||
|
||||
/* TODO: add a threshold for DMA usage */
|
||||
if (cesa_dev->caps->has_tdma)
|
||||
ret = mv_cesa_ablkcipher_dma_req_init(req, tmpl);
|
||||
else
|
||||
ret = mv_cesa_ablkcipher_std_req_init(req, tmpl);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mv_cesa_des_op(struct ablkcipher_request *req,
|
||||
struct mv_cesa_op_ctx *tmpl)
|
||||
{
|
||||
struct mv_cesa_des_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
|
||||
int ret;
|
||||
|
||||
mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_CRYPTM_DES,
|
||||
CESA_SA_DESC_CFG_CRYPTM_MSK);
|
||||
|
||||
memcpy(tmpl->ctx.blkcipher.key, ctx->key, DES_KEY_SIZE);
|
||||
|
||||
ret = mv_cesa_ablkcipher_req_init(req, tmpl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mv_cesa_queue_req(&req->base);
|
||||
if (ret && ret != -EINPROGRESS)
|
||||
mv_cesa_ablkcipher_cleanup(req);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mv_cesa_ecb_des_encrypt(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_op_ctx tmpl;
|
||||
|
||||
mv_cesa_set_op_cfg(&tmpl,
|
||||
CESA_SA_DESC_CFG_CRYPTCM_ECB |
|
||||
CESA_SA_DESC_CFG_DIR_ENC);
|
||||
|
||||
return mv_cesa_des_op(req, &tmpl);
|
||||
}
|
||||
|
||||
static int mv_cesa_ecb_des_decrypt(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_op_ctx tmpl;
|
||||
|
||||
mv_cesa_set_op_cfg(&tmpl,
|
||||
CESA_SA_DESC_CFG_CRYPTCM_ECB |
|
||||
CESA_SA_DESC_CFG_DIR_DEC);
|
||||
|
||||
return mv_cesa_des_op(req, &tmpl);
|
||||
}
|
||||
|
||||
struct crypto_alg mv_cesa_ecb_des_alg = {
|
||||
.cra_name = "ecb(des)",
|
||||
.cra_driver_name = "mv-ecb-des",
|
||||
.cra_priority = 300,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
|
||||
CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC,
|
||||
.cra_blocksize = DES_BLOCK_SIZE,
|
||||
.cra_ctxsize = sizeof(struct mv_cesa_des_ctx),
|
||||
.cra_alignmask = 0,
|
||||
.cra_type = &crypto_ablkcipher_type,
|
||||
.cra_module = THIS_MODULE,
|
||||
.cra_init = mv_cesa_ablkcipher_cra_init,
|
||||
.cra_u = {
|
||||
.ablkcipher = {
|
||||
.min_keysize = DES_KEY_SIZE,
|
||||
.max_keysize = DES_KEY_SIZE,
|
||||
.setkey = mv_cesa_des_setkey,
|
||||
.encrypt = mv_cesa_ecb_des_encrypt,
|
||||
.decrypt = mv_cesa_ecb_des_decrypt,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int mv_cesa_cbc_des_op(struct ablkcipher_request *req,
|
||||
struct mv_cesa_op_ctx *tmpl)
|
||||
{
|
||||
mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_CRYPTCM_CBC,
|
||||
CESA_SA_DESC_CFG_CRYPTCM_MSK);
|
||||
|
||||
memcpy(tmpl->ctx.blkcipher.iv, req->info, DES_BLOCK_SIZE);
|
||||
|
||||
return mv_cesa_des_op(req, tmpl);
|
||||
}
|
||||
|
||||
static int mv_cesa_cbc_des_encrypt(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_op_ctx tmpl;
|
||||
|
||||
mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_DIR_ENC);
|
||||
|
||||
return mv_cesa_cbc_des_op(req, &tmpl);
|
||||
}
|
||||
|
||||
static int mv_cesa_cbc_des_decrypt(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_op_ctx tmpl;
|
||||
|
||||
mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_DIR_DEC);
|
||||
|
||||
return mv_cesa_cbc_des_op(req, &tmpl);
|
||||
}
|
||||
|
||||
struct crypto_alg mv_cesa_cbc_des_alg = {
|
||||
.cra_name = "cbc(des)",
|
||||
.cra_driver_name = "mv-cbc-des",
|
||||
.cra_priority = 300,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
|
||||
CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC,
|
||||
.cra_blocksize = DES_BLOCK_SIZE,
|
||||
.cra_ctxsize = sizeof(struct mv_cesa_des_ctx),
|
||||
.cra_alignmask = 0,
|
||||
.cra_type = &crypto_ablkcipher_type,
|
||||
.cra_module = THIS_MODULE,
|
||||
.cra_init = mv_cesa_ablkcipher_cra_init,
|
||||
.cra_u = {
|
||||
.ablkcipher = {
|
||||
.min_keysize = DES_KEY_SIZE,
|
||||
.max_keysize = DES_KEY_SIZE,
|
||||
.ivsize = DES_BLOCK_SIZE,
|
||||
.setkey = mv_cesa_des_setkey,
|
||||
.encrypt = mv_cesa_cbc_des_encrypt,
|
||||
.decrypt = mv_cesa_cbc_des_decrypt,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int mv_cesa_des3_op(struct ablkcipher_request *req,
|
||||
struct mv_cesa_op_ctx *tmpl)
|
||||
{
|
||||
struct mv_cesa_des3_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
|
||||
int ret;
|
||||
|
||||
mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_CRYPTM_3DES,
|
||||
CESA_SA_DESC_CFG_CRYPTM_MSK);
|
||||
|
||||
memcpy(tmpl->ctx.blkcipher.key, ctx->key, DES3_EDE_KEY_SIZE);
|
||||
|
||||
ret = mv_cesa_ablkcipher_req_init(req, tmpl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mv_cesa_queue_req(&req->base);
|
||||
if (ret && ret != -EINPROGRESS)
|
||||
mv_cesa_ablkcipher_cleanup(req);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mv_cesa_ecb_des3_ede_encrypt(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_op_ctx tmpl;
|
||||
|
||||
mv_cesa_set_op_cfg(&tmpl,
|
||||
CESA_SA_DESC_CFG_CRYPTCM_ECB |
|
||||
CESA_SA_DESC_CFG_3DES_EDE |
|
||||
CESA_SA_DESC_CFG_DIR_ENC);
|
||||
|
||||
return mv_cesa_des3_op(req, &tmpl);
|
||||
}
|
||||
|
||||
static int mv_cesa_ecb_des3_ede_decrypt(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_op_ctx tmpl;
|
||||
|
||||
mv_cesa_set_op_cfg(&tmpl,
|
||||
CESA_SA_DESC_CFG_CRYPTCM_ECB |
|
||||
CESA_SA_DESC_CFG_3DES_EDE |
|
||||
CESA_SA_DESC_CFG_DIR_DEC);
|
||||
|
||||
return mv_cesa_des3_op(req, &tmpl);
|
||||
}
|
||||
|
||||
struct crypto_alg mv_cesa_ecb_des3_ede_alg = {
|
||||
.cra_name = "ecb(des3_ede)",
|
||||
.cra_driver_name = "mv-ecb-des3-ede",
|
||||
.cra_priority = 300,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
|
||||
CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC,
|
||||
.cra_blocksize = DES3_EDE_BLOCK_SIZE,
|
||||
.cra_ctxsize = sizeof(struct mv_cesa_des3_ctx),
|
||||
.cra_alignmask = 0,
|
||||
.cra_type = &crypto_ablkcipher_type,
|
||||
.cra_module = THIS_MODULE,
|
||||
.cra_init = mv_cesa_ablkcipher_cra_init,
|
||||
.cra_u = {
|
||||
.ablkcipher = {
|
||||
.min_keysize = DES3_EDE_KEY_SIZE,
|
||||
.max_keysize = DES3_EDE_KEY_SIZE,
|
||||
.ivsize = DES3_EDE_BLOCK_SIZE,
|
||||
.setkey = mv_cesa_des3_ede_setkey,
|
||||
.encrypt = mv_cesa_ecb_des3_ede_encrypt,
|
||||
.decrypt = mv_cesa_ecb_des3_ede_decrypt,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int mv_cesa_cbc_des3_op(struct ablkcipher_request *req,
|
||||
struct mv_cesa_op_ctx *tmpl)
|
||||
{
|
||||
memcpy(tmpl->ctx.blkcipher.iv, req->info, DES3_EDE_BLOCK_SIZE);
|
||||
|
||||
return mv_cesa_des3_op(req, tmpl);
|
||||
}
|
||||
|
||||
static int mv_cesa_cbc_des3_ede_encrypt(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_op_ctx tmpl;
|
||||
|
||||
mv_cesa_set_op_cfg(&tmpl,
|
||||
CESA_SA_DESC_CFG_CRYPTCM_CBC |
|
||||
CESA_SA_DESC_CFG_3DES_EDE |
|
||||
CESA_SA_DESC_CFG_DIR_ENC);
|
||||
|
||||
return mv_cesa_cbc_des3_op(req, &tmpl);
|
||||
}
|
||||
|
||||
static int mv_cesa_cbc_des3_ede_decrypt(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_op_ctx tmpl;
|
||||
|
||||
mv_cesa_set_op_cfg(&tmpl,
|
||||
CESA_SA_DESC_CFG_CRYPTCM_CBC |
|
||||
CESA_SA_DESC_CFG_3DES_EDE |
|
||||
CESA_SA_DESC_CFG_DIR_DEC);
|
||||
|
||||
return mv_cesa_cbc_des3_op(req, &tmpl);
|
||||
}
|
||||
|
||||
struct crypto_alg mv_cesa_cbc_des3_ede_alg = {
|
||||
.cra_name = "cbc(des3_ede)",
|
||||
.cra_driver_name = "mv-cbc-des3-ede",
|
||||
.cra_priority = 300,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
|
||||
CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC,
|
||||
.cra_blocksize = DES3_EDE_BLOCK_SIZE,
|
||||
.cra_ctxsize = sizeof(struct mv_cesa_des3_ctx),
|
||||
.cra_alignmask = 0,
|
||||
.cra_type = &crypto_ablkcipher_type,
|
||||
.cra_module = THIS_MODULE,
|
||||
.cra_init = mv_cesa_ablkcipher_cra_init,
|
||||
.cra_u = {
|
||||
.ablkcipher = {
|
||||
.min_keysize = DES3_EDE_KEY_SIZE,
|
||||
.max_keysize = DES3_EDE_KEY_SIZE,
|
||||
.ivsize = DES3_EDE_BLOCK_SIZE,
|
||||
.setkey = mv_cesa_des3_ede_setkey,
|
||||
.encrypt = mv_cesa_cbc_des3_ede_encrypt,
|
||||
.decrypt = mv_cesa_cbc_des3_ede_decrypt,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int mv_cesa_aes_op(struct ablkcipher_request *req,
|
||||
struct mv_cesa_op_ctx *tmpl)
|
||||
{
|
||||
struct mv_cesa_aes_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
|
||||
int ret, i;
|
||||
u32 *key;
|
||||
u32 cfg;
|
||||
|
||||
cfg = CESA_SA_DESC_CFG_CRYPTM_AES;
|
||||
|
||||
if (mv_cesa_get_op_cfg(tmpl) & CESA_SA_DESC_CFG_DIR_DEC)
|
||||
key = ctx->aes.key_dec;
|
||||
else
|
||||
key = ctx->aes.key_enc;
|
||||
|
||||
for (i = 0; i < ctx->aes.key_length / sizeof(u32); i++)
|
||||
tmpl->ctx.blkcipher.key[i] = cpu_to_le32(key[i]);
|
||||
|
||||
if (ctx->aes.key_length == 24)
|
||||
cfg |= CESA_SA_DESC_CFG_AES_LEN_192;
|
||||
else if (ctx->aes.key_length == 32)
|
||||
cfg |= CESA_SA_DESC_CFG_AES_LEN_256;
|
||||
|
||||
mv_cesa_update_op_cfg(tmpl, cfg,
|
||||
CESA_SA_DESC_CFG_CRYPTM_MSK |
|
||||
CESA_SA_DESC_CFG_AES_LEN_MSK);
|
||||
|
||||
ret = mv_cesa_ablkcipher_req_init(req, tmpl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mv_cesa_queue_req(&req->base);
|
||||
if (ret && ret != -EINPROGRESS)
|
||||
mv_cesa_ablkcipher_cleanup(req);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mv_cesa_ecb_aes_encrypt(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_op_ctx tmpl;
|
||||
|
||||
mv_cesa_set_op_cfg(&tmpl,
|
||||
CESA_SA_DESC_CFG_CRYPTCM_ECB |
|
||||
CESA_SA_DESC_CFG_DIR_ENC);
|
||||
|
||||
return mv_cesa_aes_op(req, &tmpl);
|
||||
}
|
||||
|
||||
static int mv_cesa_ecb_aes_decrypt(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_op_ctx tmpl;
|
||||
|
||||
mv_cesa_set_op_cfg(&tmpl,
|
||||
CESA_SA_DESC_CFG_CRYPTCM_ECB |
|
||||
CESA_SA_DESC_CFG_DIR_DEC);
|
||||
|
||||
return mv_cesa_aes_op(req, &tmpl);
|
||||
}
|
||||
|
||||
struct crypto_alg mv_cesa_ecb_aes_alg = {
|
||||
.cra_name = "ecb(aes)",
|
||||
.cra_driver_name = "mv-ecb-aes",
|
||||
.cra_priority = 300,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
|
||||
CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC,
|
||||
.cra_blocksize = AES_BLOCK_SIZE,
|
||||
.cra_ctxsize = sizeof(struct mv_cesa_aes_ctx),
|
||||
.cra_alignmask = 0,
|
||||
.cra_type = &crypto_ablkcipher_type,
|
||||
.cra_module = THIS_MODULE,
|
||||
.cra_init = mv_cesa_ablkcipher_cra_init,
|
||||
.cra_u = {
|
||||
.ablkcipher = {
|
||||
.min_keysize = AES_MIN_KEY_SIZE,
|
||||
.max_keysize = AES_MAX_KEY_SIZE,
|
||||
.setkey = mv_cesa_aes_setkey,
|
||||
.encrypt = mv_cesa_ecb_aes_encrypt,
|
||||
.decrypt = mv_cesa_ecb_aes_decrypt,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int mv_cesa_cbc_aes_op(struct ablkcipher_request *req,
|
||||
struct mv_cesa_op_ctx *tmpl)
|
||||
{
|
||||
mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_CRYPTCM_CBC,
|
||||
CESA_SA_DESC_CFG_CRYPTCM_MSK);
|
||||
memcpy(tmpl->ctx.blkcipher.iv, req->info, AES_BLOCK_SIZE);
|
||||
|
||||
return mv_cesa_aes_op(req, tmpl);
|
||||
}
|
||||
|
||||
static int mv_cesa_cbc_aes_encrypt(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_op_ctx tmpl;
|
||||
|
||||
mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_DIR_ENC);
|
||||
|
||||
return mv_cesa_cbc_aes_op(req, &tmpl);
|
||||
}
|
||||
|
||||
static int mv_cesa_cbc_aes_decrypt(struct ablkcipher_request *req)
|
||||
{
|
||||
struct mv_cesa_op_ctx tmpl;
|
||||
|
||||
mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_DIR_DEC);
|
||||
|
||||
return mv_cesa_cbc_aes_op(req, &tmpl);
|
||||
}
|
||||
|
||||
struct crypto_alg mv_cesa_cbc_aes_alg = {
|
||||
.cra_name = "cbc(aes)",
|
||||
.cra_driver_name = "mv-cbc-aes",
|
||||
.cra_priority = 300,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
|
||||
CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC,
|
||||
.cra_blocksize = AES_BLOCK_SIZE,
|
||||
.cra_ctxsize = sizeof(struct mv_cesa_aes_ctx),
|
||||
.cra_alignmask = 0,
|
||||
.cra_type = &crypto_ablkcipher_type,
|
||||
.cra_module = THIS_MODULE,
|
||||
.cra_init = mv_cesa_ablkcipher_cra_init,
|
||||
.cra_u = {
|
||||
.ablkcipher = {
|
||||
.min_keysize = AES_MIN_KEY_SIZE,
|
||||
.max_keysize = AES_MAX_KEY_SIZE,
|
||||
.ivsize = AES_BLOCK_SIZE,
|
||||
.setkey = mv_cesa_aes_setkey,
|
||||
.encrypt = mv_cesa_cbc_aes_encrypt,
|
||||
.decrypt = mv_cesa_cbc_aes_decrypt,
|
||||
},
|
||||
},
|
||||
};
|
1441
drivers/crypto/marvell/hash.c
Normal file
1441
drivers/crypto/marvell/hash.c
Normal file
File diff suppressed because it is too large
Load Diff
224
drivers/crypto/marvell/tdma.c
Normal file
224
drivers/crypto/marvell/tdma.c
Normal file
@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Provide TDMA helper functions used by cipher and hash algorithm
|
||||
* implementations.
|
||||
*
|
||||
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
|
||||
* Author: Arnaud Ebalard <arno@natisbad.org>
|
||||
*
|
||||
* This work is based on an initial version written by
|
||||
* Sebastian Andrzej Siewior < sebastian at breakpoint dot cc >
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "cesa.h"
|
||||
|
||||
bool mv_cesa_req_dma_iter_next_transfer(struct mv_cesa_dma_iter *iter,
|
||||
struct mv_cesa_sg_dma_iter *sgiter,
|
||||
unsigned int len)
|
||||
{
|
||||
if (!sgiter->sg)
|
||||
return false;
|
||||
|
||||
sgiter->op_offset += len;
|
||||
sgiter->offset += len;
|
||||
if (sgiter->offset == sg_dma_len(sgiter->sg)) {
|
||||
if (sg_is_last(sgiter->sg))
|
||||
return false;
|
||||
sgiter->offset = 0;
|
||||
sgiter->sg = sg_next(sgiter->sg);
|
||||
}
|
||||
|
||||
if (sgiter->op_offset == iter->op_len)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void mv_cesa_dma_step(struct mv_cesa_tdma_req *dreq)
|
||||
{
|
||||
struct mv_cesa_engine *engine = dreq->base.engine;
|
||||
|
||||
writel(0, engine->regs + CESA_SA_CFG);
|
||||
|
||||
mv_cesa_set_int_mask(engine, CESA_SA_INT_ACC0_IDMA_DONE);
|
||||
writel(CESA_TDMA_DST_BURST_128B | CESA_TDMA_SRC_BURST_128B |
|
||||
CESA_TDMA_NO_BYTE_SWAP | CESA_TDMA_EN,
|
||||
engine->regs + CESA_TDMA_CONTROL);
|
||||
|
||||
writel(CESA_SA_CFG_ACT_CH0_IDMA | CESA_SA_CFG_MULTI_PKT |
|
||||
CESA_SA_CFG_CH0_W_IDMA | CESA_SA_CFG_PARA_DIS,
|
||||
engine->regs + CESA_SA_CFG);
|
||||
writel(dreq->chain.first->cur_dma,
|
||||
engine->regs + CESA_TDMA_NEXT_ADDR);
|
||||
writel(CESA_SA_CMD_EN_CESA_SA_ACCL0, engine->regs + CESA_SA_CMD);
|
||||
}
|
||||
|
||||
void mv_cesa_dma_cleanup(struct mv_cesa_tdma_req *dreq)
|
||||
{
|
||||
struct mv_cesa_tdma_desc *tdma;
|
||||
|
||||
for (tdma = dreq->chain.first; tdma;) {
|
||||
struct mv_cesa_tdma_desc *old_tdma = tdma;
|
||||
|
||||
if (tdma->flags & CESA_TDMA_OP)
|
||||
dma_pool_free(cesa_dev->dma->op_pool, tdma->op,
|
||||
le32_to_cpu(tdma->src));
|
||||
|
||||
tdma = tdma->next;
|
||||
dma_pool_free(cesa_dev->dma->tdma_desc_pool, old_tdma,
|
||||
le32_to_cpu(old_tdma->cur_dma));
|
||||
}
|
||||
|
||||
dreq->chain.first = NULL;
|
||||
dreq->chain.last = NULL;
|
||||
}
|
||||
|
||||
void mv_cesa_dma_prepare(struct mv_cesa_tdma_req *dreq,
|
||||
struct mv_cesa_engine *engine)
|
||||
{
|
||||
struct mv_cesa_tdma_desc *tdma;
|
||||
|
||||
for (tdma = dreq->chain.first; tdma; tdma = tdma->next) {
|
||||
if (tdma->flags & CESA_TDMA_DST_IN_SRAM)
|
||||
tdma->dst = cpu_to_le32(tdma->dst + engine->sram_dma);
|
||||
|
||||
if (tdma->flags & CESA_TDMA_SRC_IN_SRAM)
|
||||
tdma->src = cpu_to_le32(tdma->src + engine->sram_dma);
|
||||
|
||||
if (tdma->flags & CESA_TDMA_OP)
|
||||
mv_cesa_adjust_op(engine, tdma->op);
|
||||
}
|
||||
}
|
||||
|
||||
static struct mv_cesa_tdma_desc *
|
||||
mv_cesa_dma_add_desc(struct mv_cesa_tdma_chain *chain, gfp_t flags)
|
||||
{
|
||||
struct mv_cesa_tdma_desc *new_tdma = NULL;
|
||||
dma_addr_t dma_handle;
|
||||
|
||||
new_tdma = dma_pool_alloc(cesa_dev->dma->tdma_desc_pool, flags,
|
||||
&dma_handle);
|
||||
if (!new_tdma)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
memset(new_tdma, 0, sizeof(*new_tdma));
|
||||
new_tdma->cur_dma = cpu_to_le32(dma_handle);
|
||||
if (chain->last) {
|
||||
chain->last->next_dma = new_tdma->cur_dma;
|
||||
chain->last->next = new_tdma;
|
||||
} else {
|
||||
chain->first = new_tdma;
|
||||
}
|
||||
|
||||
chain->last = new_tdma;
|
||||
|
||||
return new_tdma;
|
||||
}
|
||||
|
||||
struct mv_cesa_op_ctx *mv_cesa_dma_add_op(struct mv_cesa_tdma_chain *chain,
|
||||
const struct mv_cesa_op_ctx *op_templ,
|
||||
bool skip_ctx,
|
||||
gfp_t flags)
|
||||
{
|
||||
struct mv_cesa_tdma_desc *tdma;
|
||||
struct mv_cesa_op_ctx *op;
|
||||
dma_addr_t dma_handle;
|
||||
|
||||
tdma = mv_cesa_dma_add_desc(chain, flags);
|
||||
if (IS_ERR(tdma))
|
||||
return ERR_CAST(tdma);
|
||||
|
||||
op = dma_pool_alloc(cesa_dev->dma->op_pool, flags, &dma_handle);
|
||||
if (!op)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
*op = *op_templ;
|
||||
|
||||
tdma = chain->last;
|
||||
tdma->op = op;
|
||||
tdma->byte_cnt = (skip_ctx ? sizeof(op->desc) : sizeof(*op)) | BIT(31);
|
||||
tdma->src = dma_handle;
|
||||
tdma->flags = CESA_TDMA_DST_IN_SRAM | CESA_TDMA_OP;
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
int mv_cesa_dma_add_data_transfer(struct mv_cesa_tdma_chain *chain,
|
||||
dma_addr_t dst, dma_addr_t src, u32 size,
|
||||
u32 flags, gfp_t gfp_flags)
|
||||
{
|
||||
struct mv_cesa_tdma_desc *tdma;
|
||||
|
||||
tdma = mv_cesa_dma_add_desc(chain, gfp_flags);
|
||||
if (IS_ERR(tdma))
|
||||
return PTR_ERR(tdma);
|
||||
|
||||
tdma->byte_cnt = size | BIT(31);
|
||||
tdma->src = src;
|
||||
tdma->dst = dst;
|
||||
|
||||
flags &= (CESA_TDMA_DST_IN_SRAM | CESA_TDMA_SRC_IN_SRAM);
|
||||
tdma->flags = flags | CESA_TDMA_DATA;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mv_cesa_dma_add_dummy_launch(struct mv_cesa_tdma_chain *chain,
|
||||
u32 flags)
|
||||
{
|
||||
struct mv_cesa_tdma_desc *tdma;
|
||||
|
||||
tdma = mv_cesa_dma_add_desc(chain, flags);
|
||||
if (IS_ERR(tdma))
|
||||
return PTR_ERR(tdma);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mv_cesa_dma_add_dummy_end(struct mv_cesa_tdma_chain *chain, u32 flags)
|
||||
{
|
||||
struct mv_cesa_tdma_desc *tdma;
|
||||
|
||||
tdma = mv_cesa_dma_add_desc(chain, flags);
|
||||
if (IS_ERR(tdma))
|
||||
return PTR_ERR(tdma);
|
||||
|
||||
tdma->byte_cnt = BIT(31);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mv_cesa_dma_add_op_transfers(struct mv_cesa_tdma_chain *chain,
|
||||
struct mv_cesa_dma_iter *dma_iter,
|
||||
struct mv_cesa_sg_dma_iter *sgiter,
|
||||
gfp_t gfp_flags)
|
||||
{
|
||||
u32 flags = sgiter->dir == DMA_TO_DEVICE ?
|
||||
CESA_TDMA_DST_IN_SRAM : CESA_TDMA_SRC_IN_SRAM;
|
||||
unsigned int len;
|
||||
|
||||
do {
|
||||
dma_addr_t dst, src;
|
||||
int ret;
|
||||
|
||||
len = mv_cesa_req_dma_iter_transfer_len(dma_iter, sgiter);
|
||||
if (sgiter->dir == DMA_TO_DEVICE) {
|
||||
dst = CESA_SA_DATA_SRAM_OFFSET + sgiter->op_offset;
|
||||
src = sg_dma_address(sgiter->sg) + sgiter->offset;
|
||||
} else {
|
||||
dst = sg_dma_address(sgiter->sg) + sgiter->offset;
|
||||
src = CESA_SA_DATA_SRAM_OFFSET + sgiter->op_offset;
|
||||
}
|
||||
|
||||
ret = mv_cesa_dma_add_data_transfer(chain, dst, src, len,
|
||||
flags, gfp_flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
} while (mv_cesa_req_dma_iter_next_transfer(dma_iter, sgiter, len));
|
||||
|
||||
return 0;
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
#include <crypto/aes.h>
|
||||
#include <crypto/algapi.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kthread.h>
|
||||
@ -29,6 +30,8 @@
|
||||
#define MAX_HW_HASH_SIZE 0xFFFF
|
||||
#define MV_CESA_EXPIRE 500 /* msec */
|
||||
|
||||
#define MV_CESA_DEFAULT_SRAM_SIZE 2048
|
||||
|
||||
/*
|
||||
* STM:
|
||||
* /---------------------------------------\
|
||||
@ -83,6 +86,8 @@ struct req_progress {
|
||||
struct crypto_priv {
|
||||
void __iomem *reg;
|
||||
void __iomem *sram;
|
||||
struct gen_pool *sram_pool;
|
||||
dma_addr_t sram_dma;
|
||||
int irq;
|
||||
struct clk *clk;
|
||||
struct task_struct *queue_th;
|
||||
@ -595,7 +600,7 @@ static int queue_manag(void *data)
|
||||
cpg->eng_st = ENGINE_IDLE;
|
||||
do {
|
||||
struct crypto_async_request *async_req = NULL;
|
||||
struct crypto_async_request *backlog;
|
||||
struct crypto_async_request *backlog = NULL;
|
||||
|
||||
__set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
@ -1019,6 +1024,39 @@ static struct ahash_alg mv_hmac_sha1_alg = {
|
||||
}
|
||||
};
|
||||
|
||||
static int mv_cesa_get_sram(struct platform_device *pdev,
|
||||
struct crypto_priv *cp)
|
||||
{
|
||||
struct resource *res;
|
||||
u32 sram_size = MV_CESA_DEFAULT_SRAM_SIZE;
|
||||
|
||||
of_property_read_u32(pdev->dev.of_node, "marvell,crypto-sram-size",
|
||||
&sram_size);
|
||||
|
||||
cp->sram_size = sram_size;
|
||||
cp->sram_pool = of_get_named_gen_pool(pdev->dev.of_node,
|
||||
"marvell,crypto-srams", 0);
|
||||
if (cp->sram_pool) {
|
||||
cp->sram = gen_pool_dma_alloc(cp->sram_pool, sram_size,
|
||||
&cp->sram_dma);
|
||||
if (cp->sram)
|
||||
return 0;
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"sram");
|
||||
if (!res || resource_size(res) < cp->sram_size)
|
||||
return -EINVAL;
|
||||
|
||||
cp->sram = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(cp->sram))
|
||||
return PTR_ERR(cp->sram);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct crypto_priv *cp;
|
||||
@ -1041,24 +1079,17 @@ static int mv_probe(struct platform_device *pdev)
|
||||
|
||||
spin_lock_init(&cp->lock);
|
||||
crypto_init_queue(&cp->queue, 50);
|
||||
cp->reg = ioremap(res->start, resource_size(res));
|
||||
if (!cp->reg) {
|
||||
ret = -ENOMEM;
|
||||
cp->reg = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(cp->reg)) {
|
||||
ret = PTR_ERR(cp->reg);
|
||||
goto err;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram");
|
||||
if (!res) {
|
||||
ret = -ENXIO;
|
||||
goto err_unmap_reg;
|
||||
}
|
||||
cp->sram_size = resource_size(res);
|
||||
ret = mv_cesa_get_sram(pdev, cp);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
cp->max_req_size = cp->sram_size - SRAM_CFG_SPACE;
|
||||
cp->sram = ioremap(res->start, cp->sram_size);
|
||||
if (!cp->sram) {
|
||||
ret = -ENOMEM;
|
||||
goto err_unmap_reg;
|
||||
}
|
||||
|
||||
if (pdev->dev.of_node)
|
||||
irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
|
||||
@ -1066,7 +1097,7 @@ static int mv_probe(struct platform_device *pdev)
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0 || irq == NO_IRQ) {
|
||||
ret = irq;
|
||||
goto err_unmap_sram;
|
||||
goto err;
|
||||
}
|
||||
cp->irq = irq;
|
||||
|
||||
@ -1076,7 +1107,7 @@ static int mv_probe(struct platform_device *pdev)
|
||||
cp->queue_th = kthread_run(queue_manag, cp, "mv_crypto");
|
||||
if (IS_ERR(cp->queue_th)) {
|
||||
ret = PTR_ERR(cp->queue_th);
|
||||
goto err_unmap_sram;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = request_irq(irq, crypto_int, 0, dev_name(&pdev->dev),
|
||||
@ -1134,10 +1165,6 @@ err_irq:
|
||||
}
|
||||
err_thread:
|
||||
kthread_stop(cp->queue_th);
|
||||
err_unmap_sram:
|
||||
iounmap(cp->sram);
|
||||
err_unmap_reg:
|
||||
iounmap(cp->reg);
|
||||
err:
|
||||
kfree(cp);
|
||||
cpg = NULL;
|
||||
@ -1157,8 +1184,6 @@ static int mv_remove(struct platform_device *pdev)
|
||||
kthread_stop(cp->queue_th);
|
||||
free_irq(cp->irq, cp);
|
||||
memset(cp->sram, 0, cp->sram_size);
|
||||
iounmap(cp->sram);
|
||||
iounmap(cp->reg);
|
||||
|
||||
if (!IS_ERR(cp->clk)) {
|
||||
clk_disable_unprepare(cp->clk);
|
||||
@ -1172,6 +1197,8 @@ static int mv_remove(struct platform_device *pdev)
|
||||
|
||||
static const struct of_device_id mv_cesa_of_match_table[] = {
|
||||
{ .compatible = "marvell,orion-crypto", },
|
||||
{ .compatible = "marvell,kirkwood-crypto", },
|
||||
{ .compatible = "marvell,dove-crypto", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mv_cesa_of_match_table);
|
||||
|
@ -1281,10 +1281,10 @@ static const char md5_zero[MD5_DIGEST_SIZE] = {
|
||||
0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e,
|
||||
};
|
||||
static const u32 md5_init[MD5_HASH_WORDS] = {
|
||||
cpu_to_le32(0x67452301),
|
||||
cpu_to_le32(0xefcdab89),
|
||||
cpu_to_le32(0x98badcfe),
|
||||
cpu_to_le32(0x10325476),
|
||||
cpu_to_le32(MD5_H0),
|
||||
cpu_to_le32(MD5_H1),
|
||||
cpu_to_le32(MD5_H2),
|
||||
cpu_to_le32(MD5_H3),
|
||||
};
|
||||
static const char sha1_zero[SHA1_DIGEST_SIZE] = {
|
||||
0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32,
|
||||
|
@ -1,26 +1,55 @@
|
||||
|
||||
config CRYPTO_DEV_NX_ENCRYPT
|
||||
tristate "Encryption acceleration support"
|
||||
depends on PPC64 && IBMVIO
|
||||
tristate "Encryption acceleration support on pSeries platform"
|
||||
depends on PPC_PSERIES && IBMVIO && !CPU_LITTLE_ENDIAN
|
||||
default y
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_CBC
|
||||
select CRYPTO_ECB
|
||||
select CRYPTO_CCM
|
||||
select CRYPTO_GCM
|
||||
select CRYPTO_AUTHENC
|
||||
select CRYPTO_XCBC
|
||||
select CRYPTO_SHA256
|
||||
select CRYPTO_SHA512
|
||||
help
|
||||
Support for Power7+ in-Nest encryption acceleration. This
|
||||
module supports acceleration for AES and SHA2 algorithms. If you
|
||||
choose 'M' here, this module will be called nx_crypto.
|
||||
Support for PowerPC Nest (NX) encryption acceleration. This
|
||||
module supports acceleration for AES and SHA2 algorithms on
|
||||
the pSeries platform. If you choose 'M' here, this module
|
||||
will be called nx_crypto.
|
||||
|
||||
config CRYPTO_DEV_NX_COMPRESS
|
||||
tristate "Compression acceleration support"
|
||||
depends on PPC64 && IBMVIO
|
||||
default y
|
||||
help
|
||||
Support for Power7+ in-Nest compression acceleration. This
|
||||
module supports acceleration for AES and SHA2 algorithms. If you
|
||||
choose 'M' here, this module will be called nx_compress.
|
||||
Support for PowerPC Nest (NX) compression acceleration. This
|
||||
module supports acceleration for compressing memory with the 842
|
||||
algorithm. One of the platform drivers must be selected also.
|
||||
If you choose 'M' here, this module will be called nx_compress.
|
||||
|
||||
if CRYPTO_DEV_NX_COMPRESS
|
||||
|
||||
config CRYPTO_DEV_NX_COMPRESS_PSERIES
|
||||
tristate "Compression acceleration support on pSeries platform"
|
||||
depends on PPC_PSERIES && IBMVIO
|
||||
default y
|
||||
help
|
||||
Support for PowerPC Nest (NX) compression acceleration. This
|
||||
module supports acceleration for compressing memory with the 842
|
||||
algorithm. This supports NX hardware on the pSeries platform.
|
||||
If you choose 'M' here, this module will be called nx_compress_pseries.
|
||||
|
||||
config CRYPTO_DEV_NX_COMPRESS_POWERNV
|
||||
tristate "Compression acceleration support on PowerNV platform"
|
||||
depends on PPC_POWERNV
|
||||
default y
|
||||
help
|
||||
Support for PowerPC Nest (NX) compression acceleration. This
|
||||
module supports acceleration for compressing memory with the 842
|
||||
algorithm. This supports NX hardware on the PowerNV platform.
|
||||
If you choose 'M' here, this module will be called nx_compress_powernv.
|
||||
|
||||
config CRYPTO_DEV_NX_COMPRESS_CRYPTO
|
||||
tristate "Compression acceleration cryptographic interface"
|
||||
select CRYPTO_ALGAPI
|
||||
select 842_DECOMPRESS
|
||||
default y
|
||||
help
|
||||
Support for PowerPC Nest (NX) accelerators using the cryptographic
|
||||
API. If you choose 'M' here, this module will be called
|
||||
nx_compress_crypto.
|
||||
|
||||
endif
|
||||
|
@ -10,5 +10,12 @@ nx-crypto-objs := nx.o \
|
||||
nx-sha256.o \
|
||||
nx-sha512.o
|
||||
|
||||
obj-$(CONFIG_CRYPTO_DEV_NX_COMPRESS) += nx-compress.o
|
||||
obj-$(CONFIG_CRYPTO_DEV_NX_COMPRESS) += nx-compress.o nx-compress-platform.o
|
||||
obj-$(CONFIG_CRYPTO_DEV_NX_COMPRESS_PSERIES) += nx-compress-pseries.o
|
||||
obj-$(CONFIG_CRYPTO_DEV_NX_COMPRESS_POWERNV) += nx-compress-powernv.o
|
||||
obj-$(CONFIG_CRYPTO_DEV_NX_COMPRESS_CRYPTO) += nx-compress-crypto.o
|
||||
nx-compress-objs := nx-842.o
|
||||
nx-compress-platform-objs := nx-842-platform.o
|
||||
nx-compress-pseries-objs := nx-842-pseries.o
|
||||
nx-compress-powernv-objs := nx-842-powernv.o
|
||||
nx-compress-crypto-objs := nx-842-crypto.o
|
||||
|
580
drivers/crypto/nx/nx-842-crypto.c
Normal file
580
drivers/crypto/nx/nx-842-crypto.c
Normal file
@ -0,0 +1,580 @@
|
||||
/*
|
||||
* Cryptographic API for the NX-842 hardware compression.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* Copyright (C) IBM Corporation, 2011-2015
|
||||
*
|
||||
* Original Authors: Robert Jennings <rcj@linux.vnet.ibm.com>
|
||||
* Seth Jennings <sjenning@linux.vnet.ibm.com>
|
||||
*
|
||||
* Rewrite: Dan Streetman <ddstreet@ieee.org>
|
||||
*
|
||||
* This is an interface to the NX-842 compression hardware in PowerPC
|
||||
* processors. Most of the complexity of this drvier is due to the fact that
|
||||
* the NX-842 compression hardware requires the input and output data buffers
|
||||
* to be specifically aligned, to be a specific multiple in length, and within
|
||||
* specific minimum and maximum lengths. Those restrictions, provided by the
|
||||
* nx-842 driver via nx842_constraints, mean this driver must use bounce
|
||||
* buffers and headers to correct misaligned in or out buffers, and to split
|
||||
* input buffers that are too large.
|
||||
*
|
||||
* This driver will fall back to software decompression if the hardware
|
||||
* decompression fails, so this driver's decompression should never fail as
|
||||
* long as the provided compressed buffer is valid. Any compressed buffer
|
||||
* created by this driver will have a header (except ones where the input
|
||||
* perfectly matches the constraints); so users of this driver cannot simply
|
||||
* pass a compressed buffer created by this driver over to the 842 software
|
||||
* decompression library. Instead, users must use this driver to decompress;
|
||||
* if the hardware fails or is unavailable, the compressed buffer will be
|
||||
* parsed and the header removed, and the raw 842 buffer(s) passed to the 842
|
||||
* software decompression library.
|
||||
*
|
||||
* This does not fall back to software compression, however, since the caller
|
||||
* of this function is specifically requesting hardware compression; if the
|
||||
* hardware compression fails, the caller can fall back to software
|
||||
* compression, and the raw 842 compressed buffer that the software compressor
|
||||
* creates can be passed to this driver for hardware decompression; any
|
||||
* buffer without our specific header magic is assumed to be a raw 842 buffer
|
||||
* and passed directly to the hardware. Note that the software compression
|
||||
* library will produce a compressed buffer that is incompatible with the
|
||||
* hardware decompressor if the original input buffer length is not a multiple
|
||||
* of 8; if such a compressed buffer is passed to this driver for
|
||||
* decompression, the hardware will reject it and this driver will then pass
|
||||
* it over to the software library for decompression.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/sw842.h>
|
||||
#include <linux/ratelimit.h>
|
||||
|
||||
#include "nx-842.h"
|
||||
|
||||
/* The first 5 bits of this magic are 0x1f, which is an invalid 842 5-bit
|
||||
* template (see lib/842/842.h), so this magic number will never appear at
|
||||
* the start of a raw 842 compressed buffer. That is important, as any buffer
|
||||
* passed to us without this magic is assumed to be a raw 842 compressed
|
||||
* buffer, and passed directly to the hardware to decompress.
|
||||
*/
|
||||
#define NX842_CRYPTO_MAGIC (0xf842)
|
||||
#define NX842_CRYPTO_GROUP_MAX (0x20)
|
||||
#define NX842_CRYPTO_HEADER_SIZE(g) \
|
||||
(sizeof(struct nx842_crypto_header) + \
|
||||
sizeof(struct nx842_crypto_header_group) * (g))
|
||||
#define NX842_CRYPTO_HEADER_MAX_SIZE \
|
||||
NX842_CRYPTO_HEADER_SIZE(NX842_CRYPTO_GROUP_MAX)
|
||||
|
||||
/* bounce buffer size */
|
||||
#define BOUNCE_BUFFER_ORDER (2)
|
||||
#define BOUNCE_BUFFER_SIZE \
|
||||
((unsigned int)(PAGE_SIZE << BOUNCE_BUFFER_ORDER))
|
||||
|
||||
/* try longer on comp because we can fallback to sw decomp if hw is busy */
|
||||
#define COMP_BUSY_TIMEOUT (250) /* ms */
|
||||
#define DECOMP_BUSY_TIMEOUT (50) /* ms */
|
||||
|
||||
struct nx842_crypto_header_group {
|
||||
__be16 padding; /* unused bytes at start of group */
|
||||
__be32 compressed_length; /* compressed bytes in group */
|
||||
__be32 uncompressed_length; /* bytes after decompression */
|
||||
} __packed;
|
||||
|
||||
struct nx842_crypto_header {
|
||||
__be16 magic; /* NX842_CRYPTO_MAGIC */
|
||||
__be16 ignore; /* decompressed end bytes to ignore */
|
||||
u8 groups; /* total groups in this header */
|
||||
struct nx842_crypto_header_group group[];
|
||||
} __packed;
|
||||
|
||||
struct nx842_crypto_param {
|
||||
u8 *in;
|
||||
unsigned int iremain;
|
||||
u8 *out;
|
||||
unsigned int oremain;
|
||||
unsigned int ototal;
|
||||
};
|
||||
|
||||
static int update_param(struct nx842_crypto_param *p,
|
||||
unsigned int slen, unsigned int dlen)
|
||||
{
|
||||
if (p->iremain < slen)
|
||||
return -EOVERFLOW;
|
||||
if (p->oremain < dlen)
|
||||
return -ENOSPC;
|
||||
|
||||
p->in += slen;
|
||||
p->iremain -= slen;
|
||||
p->out += dlen;
|
||||
p->oremain -= dlen;
|
||||
p->ototal += dlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct nx842_crypto_ctx {
|
||||
u8 *wmem;
|
||||
u8 *sbounce, *dbounce;
|
||||
|
||||
struct nx842_crypto_header header;
|
||||
struct nx842_crypto_header_group group[NX842_CRYPTO_GROUP_MAX];
|
||||
};
|
||||
|
||||
static int nx842_crypto_init(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
|
||||
ctx->wmem = kmalloc(nx842_workmem_size(), GFP_KERNEL);
|
||||
ctx->sbounce = (u8 *)__get_free_pages(GFP_KERNEL, BOUNCE_BUFFER_ORDER);
|
||||
ctx->dbounce = (u8 *)__get_free_pages(GFP_KERNEL, BOUNCE_BUFFER_ORDER);
|
||||
if (!ctx->wmem || !ctx->sbounce || !ctx->dbounce) {
|
||||
kfree(ctx->wmem);
|
||||
free_page((unsigned long)ctx->sbounce);
|
||||
free_page((unsigned long)ctx->dbounce);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nx842_crypto_exit(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
|
||||
kfree(ctx->wmem);
|
||||
free_page((unsigned long)ctx->sbounce);
|
||||
free_page((unsigned long)ctx->dbounce);
|
||||
}
|
||||
|
||||
static int read_constraints(struct nx842_constraints *c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = nx842_constraints(c);
|
||||
if (ret) {
|
||||
pr_err_ratelimited("could not get nx842 constraints : %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* limit maximum, to always have enough bounce buffer to decompress */
|
||||
if (c->maximum > BOUNCE_BUFFER_SIZE) {
|
||||
c->maximum = BOUNCE_BUFFER_SIZE;
|
||||
pr_info_once("limiting nx842 maximum to %x\n", c->maximum);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nx842_crypto_add_header(struct nx842_crypto_header *hdr, u8 *buf)
|
||||
{
|
||||
int s = NX842_CRYPTO_HEADER_SIZE(hdr->groups);
|
||||
|
||||
/* compress should have added space for header */
|
||||
if (s > be16_to_cpu(hdr->group[0].padding)) {
|
||||
pr_err("Internal error: no space for header\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(buf, hdr, s);
|
||||
|
||||
print_hex_dump_debug("header ", DUMP_PREFIX_OFFSET, 16, 1, buf, s, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int compress(struct nx842_crypto_ctx *ctx,
|
||||
struct nx842_crypto_param *p,
|
||||
struct nx842_crypto_header_group *g,
|
||||
struct nx842_constraints *c,
|
||||
u16 *ignore,
|
||||
unsigned int hdrsize)
|
||||
{
|
||||
unsigned int slen = p->iremain, dlen = p->oremain, tmplen;
|
||||
unsigned int adj_slen = slen;
|
||||
u8 *src = p->in, *dst = p->out;
|
||||
int ret, dskip = 0;
|
||||
ktime_t timeout;
|
||||
|
||||
if (p->iremain == 0)
|
||||
return -EOVERFLOW;
|
||||
|
||||
if (p->oremain == 0 || hdrsize + c->minimum > dlen)
|
||||
return -ENOSPC;
|
||||
|
||||
if (slen % c->multiple)
|
||||
adj_slen = round_up(slen, c->multiple);
|
||||
if (slen < c->minimum)
|
||||
adj_slen = c->minimum;
|
||||
if (slen > c->maximum)
|
||||
adj_slen = slen = c->maximum;
|
||||
if (adj_slen > slen || (u64)src % c->alignment) {
|
||||
adj_slen = min(adj_slen, BOUNCE_BUFFER_SIZE);
|
||||
slen = min(slen, BOUNCE_BUFFER_SIZE);
|
||||
if (adj_slen > slen)
|
||||
memset(ctx->sbounce + slen, 0, adj_slen - slen);
|
||||
memcpy(ctx->sbounce, src, slen);
|
||||
src = ctx->sbounce;
|
||||
slen = adj_slen;
|
||||
pr_debug("using comp sbounce buffer, len %x\n", slen);
|
||||
}
|
||||
|
||||
dst += hdrsize;
|
||||
dlen -= hdrsize;
|
||||
|
||||
if ((u64)dst % c->alignment) {
|
||||
dskip = (int)(PTR_ALIGN(dst, c->alignment) - dst);
|
||||
dst += dskip;
|
||||
dlen -= dskip;
|
||||
}
|
||||
if (dlen % c->multiple)
|
||||
dlen = round_down(dlen, c->multiple);
|
||||
if (dlen < c->minimum) {
|
||||
nospc:
|
||||
dst = ctx->dbounce;
|
||||
dlen = min(p->oremain, BOUNCE_BUFFER_SIZE);
|
||||
dlen = round_down(dlen, c->multiple);
|
||||
dskip = 0;
|
||||
pr_debug("using comp dbounce buffer, len %x\n", dlen);
|
||||
}
|
||||
if (dlen > c->maximum)
|
||||
dlen = c->maximum;
|
||||
|
||||
tmplen = dlen;
|
||||
timeout = ktime_add_ms(ktime_get(), COMP_BUSY_TIMEOUT);
|
||||
do {
|
||||
dlen = tmplen; /* reset dlen, if we're retrying */
|
||||
ret = nx842_compress(src, slen, dst, &dlen, ctx->wmem);
|
||||
/* possibly we should reduce the slen here, instead of
|
||||
* retrying with the dbounce buffer?
|
||||
*/
|
||||
if (ret == -ENOSPC && dst != ctx->dbounce)
|
||||
goto nospc;
|
||||
} while (ret == -EBUSY && ktime_before(ktime_get(), timeout));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dskip += hdrsize;
|
||||
|
||||
if (dst == ctx->dbounce)
|
||||
memcpy(p->out + dskip, dst, dlen);
|
||||
|
||||
g->padding = cpu_to_be16(dskip);
|
||||
g->compressed_length = cpu_to_be32(dlen);
|
||||
g->uncompressed_length = cpu_to_be32(slen);
|
||||
|
||||
if (p->iremain < slen) {
|
||||
*ignore = slen - p->iremain;
|
||||
slen = p->iremain;
|
||||
}
|
||||
|
||||
pr_debug("compress slen %x ignore %x dlen %x padding %x\n",
|
||||
slen, *ignore, dlen, dskip);
|
||||
|
||||
return update_param(p, slen, dskip + dlen);
|
||||
}
|
||||
|
||||
static int nx842_crypto_compress(struct crypto_tfm *tfm,
|
||||
const u8 *src, unsigned int slen,
|
||||
u8 *dst, unsigned int *dlen)
|
||||
{
|
||||
struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
struct nx842_crypto_header *hdr = &ctx->header;
|
||||
struct nx842_crypto_param p;
|
||||
struct nx842_constraints c;
|
||||
unsigned int groups, hdrsize, h;
|
||||
int ret, n;
|
||||
bool add_header;
|
||||
u16 ignore = 0;
|
||||
|
||||
p.in = (u8 *)src;
|
||||
p.iremain = slen;
|
||||
p.out = dst;
|
||||
p.oremain = *dlen;
|
||||
p.ototal = 0;
|
||||
|
||||
*dlen = 0;
|
||||
|
||||
ret = read_constraints(&c);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
groups = min_t(unsigned int, NX842_CRYPTO_GROUP_MAX,
|
||||
DIV_ROUND_UP(p.iremain, c.maximum));
|
||||
hdrsize = NX842_CRYPTO_HEADER_SIZE(groups);
|
||||
|
||||
/* skip adding header if the buffers meet all constraints */
|
||||
add_header = (p.iremain % c.multiple ||
|
||||
p.iremain < c.minimum ||
|
||||
p.iremain > c.maximum ||
|
||||
(u64)p.in % c.alignment ||
|
||||
p.oremain % c.multiple ||
|
||||
p.oremain < c.minimum ||
|
||||
p.oremain > c.maximum ||
|
||||
(u64)p.out % c.alignment);
|
||||
|
||||
hdr->magic = cpu_to_be16(NX842_CRYPTO_MAGIC);
|
||||
hdr->groups = 0;
|
||||
hdr->ignore = 0;
|
||||
|
||||
while (p.iremain > 0) {
|
||||
n = hdr->groups++;
|
||||
if (hdr->groups > NX842_CRYPTO_GROUP_MAX)
|
||||
return -ENOSPC;
|
||||
|
||||
/* header goes before first group */
|
||||
h = !n && add_header ? hdrsize : 0;
|
||||
|
||||
if (ignore)
|
||||
pr_warn("interal error, ignore is set %x\n", ignore);
|
||||
|
||||
ret = compress(ctx, &p, &hdr->group[n], &c, &ignore, h);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!add_header && hdr->groups > 1) {
|
||||
pr_err("Internal error: No header but multiple groups\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* ignore indicates the input stream needed to be padded */
|
||||
hdr->ignore = cpu_to_be16(ignore);
|
||||
if (ignore)
|
||||
pr_debug("marked %d bytes as ignore\n", ignore);
|
||||
|
||||
if (add_header)
|
||||
ret = nx842_crypto_add_header(hdr, dst);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*dlen = p.ototal;
|
||||
|
||||
pr_debug("compress total slen %x dlen %x\n", slen, *dlen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decompress(struct nx842_crypto_ctx *ctx,
|
||||
struct nx842_crypto_param *p,
|
||||
struct nx842_crypto_header_group *g,
|
||||
struct nx842_constraints *c,
|
||||
u16 ignore,
|
||||
bool usehw)
|
||||
{
|
||||
unsigned int slen = be32_to_cpu(g->compressed_length);
|
||||
unsigned int required_len = be32_to_cpu(g->uncompressed_length);
|
||||
unsigned int dlen = p->oremain, tmplen;
|
||||
unsigned int adj_slen = slen;
|
||||
u8 *src = p->in, *dst = p->out;
|
||||
u16 padding = be16_to_cpu(g->padding);
|
||||
int ret, spadding = 0, dpadding = 0;
|
||||
ktime_t timeout;
|
||||
|
||||
if (!slen || !required_len)
|
||||
return -EINVAL;
|
||||
|
||||
if (p->iremain <= 0 || padding + slen > p->iremain)
|
||||
return -EOVERFLOW;
|
||||
|
||||
if (p->oremain <= 0 || required_len - ignore > p->oremain)
|
||||
return -ENOSPC;
|
||||
|
||||
src += padding;
|
||||
|
||||
if (!usehw)
|
||||
goto usesw;
|
||||
|
||||
if (slen % c->multiple)
|
||||
adj_slen = round_up(slen, c->multiple);
|
||||
if (slen < c->minimum)
|
||||
adj_slen = c->minimum;
|
||||
if (slen > c->maximum)
|
||||
goto usesw;
|
||||
if (slen < adj_slen || (u64)src % c->alignment) {
|
||||
/* we can append padding bytes because the 842 format defines
|
||||
* an "end" template (see lib/842/842_decompress.c) and will
|
||||
* ignore any bytes following it.
|
||||
*/
|
||||
if (slen < adj_slen)
|
||||
memset(ctx->sbounce + slen, 0, adj_slen - slen);
|
||||
memcpy(ctx->sbounce, src, slen);
|
||||
src = ctx->sbounce;
|
||||
spadding = adj_slen - slen;
|
||||
slen = adj_slen;
|
||||
pr_debug("using decomp sbounce buffer, len %x\n", slen);
|
||||
}
|
||||
|
||||
if (dlen % c->multiple)
|
||||
dlen = round_down(dlen, c->multiple);
|
||||
if (dlen < required_len || (u64)dst % c->alignment) {
|
||||
dst = ctx->dbounce;
|
||||
dlen = min(required_len, BOUNCE_BUFFER_SIZE);
|
||||
pr_debug("using decomp dbounce buffer, len %x\n", dlen);
|
||||
}
|
||||
if (dlen < c->minimum)
|
||||
goto usesw;
|
||||
if (dlen > c->maximum)
|
||||
dlen = c->maximum;
|
||||
|
||||
tmplen = dlen;
|
||||
timeout = ktime_add_ms(ktime_get(), DECOMP_BUSY_TIMEOUT);
|
||||
do {
|
||||
dlen = tmplen; /* reset dlen, if we're retrying */
|
||||
ret = nx842_decompress(src, slen, dst, &dlen, ctx->wmem);
|
||||
} while (ret == -EBUSY && ktime_before(ktime_get(), timeout));
|
||||
if (ret) {
|
||||
usesw:
|
||||
/* reset everything, sw doesn't have constraints */
|
||||
src = p->in + padding;
|
||||
slen = be32_to_cpu(g->compressed_length);
|
||||
spadding = 0;
|
||||
dst = p->out;
|
||||
dlen = p->oremain;
|
||||
dpadding = 0;
|
||||
if (dlen < required_len) { /* have ignore bytes */
|
||||
dst = ctx->dbounce;
|
||||
dlen = BOUNCE_BUFFER_SIZE;
|
||||
}
|
||||
pr_info_ratelimited("using software 842 decompression\n");
|
||||
ret = sw842_decompress(src, slen, dst, &dlen);
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
slen -= spadding;
|
||||
|
||||
dlen -= ignore;
|
||||
if (ignore)
|
||||
pr_debug("ignoring last %x bytes\n", ignore);
|
||||
|
||||
if (dst == ctx->dbounce)
|
||||
memcpy(p->out, dst, dlen);
|
||||
|
||||
pr_debug("decompress slen %x padding %x dlen %x ignore %x\n",
|
||||
slen, padding, dlen, ignore);
|
||||
|
||||
return update_param(p, slen + padding, dlen);
|
||||
}
|
||||
|
||||
static int nx842_crypto_decompress(struct crypto_tfm *tfm,
|
||||
const u8 *src, unsigned int slen,
|
||||
u8 *dst, unsigned int *dlen)
|
||||
{
|
||||
struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
struct nx842_crypto_header *hdr;
|
||||
struct nx842_crypto_param p;
|
||||
struct nx842_constraints c;
|
||||
int n, ret, hdr_len;
|
||||
u16 ignore = 0;
|
||||
bool usehw = true;
|
||||
|
||||
p.in = (u8 *)src;
|
||||
p.iremain = slen;
|
||||
p.out = dst;
|
||||
p.oremain = *dlen;
|
||||
p.ototal = 0;
|
||||
|
||||
*dlen = 0;
|
||||
|
||||
if (read_constraints(&c))
|
||||
usehw = false;
|
||||
|
||||
hdr = (struct nx842_crypto_header *)src;
|
||||
|
||||
/* If it doesn't start with our header magic number, assume it's a raw
|
||||
* 842 compressed buffer and pass it directly to the hardware driver
|
||||
*/
|
||||
if (be16_to_cpu(hdr->magic) != NX842_CRYPTO_MAGIC) {
|
||||
struct nx842_crypto_header_group g = {
|
||||
.padding = 0,
|
||||
.compressed_length = cpu_to_be32(p.iremain),
|
||||
.uncompressed_length = cpu_to_be32(p.oremain),
|
||||
};
|
||||
|
||||
ret = decompress(ctx, &p, &g, &c, 0, usehw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*dlen = p.ototal;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!hdr->groups) {
|
||||
pr_err("header has no groups\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (hdr->groups > NX842_CRYPTO_GROUP_MAX) {
|
||||
pr_err("header has too many groups %x, max %x\n",
|
||||
hdr->groups, NX842_CRYPTO_GROUP_MAX);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hdr_len = NX842_CRYPTO_HEADER_SIZE(hdr->groups);
|
||||
if (hdr_len > slen)
|
||||
return -EOVERFLOW;
|
||||
|
||||
memcpy(&ctx->header, src, hdr_len);
|
||||
hdr = &ctx->header;
|
||||
|
||||
for (n = 0; n < hdr->groups; n++) {
|
||||
/* ignore applies to last group */
|
||||
if (n + 1 == hdr->groups)
|
||||
ignore = be16_to_cpu(hdr->ignore);
|
||||
|
||||
ret = decompress(ctx, &p, &hdr->group[n], &c, ignore, usehw);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
*dlen = p.ototal;
|
||||
|
||||
pr_debug("decompress total slen %x dlen %x\n", slen, *dlen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct crypto_alg alg = {
|
||||
.cra_name = "842",
|
||||
.cra_driver_name = "842-nx",
|
||||
.cra_priority = 300,
|
||||
.cra_flags = CRYPTO_ALG_TYPE_COMPRESS,
|
||||
.cra_ctxsize = sizeof(struct nx842_crypto_ctx),
|
||||
.cra_module = THIS_MODULE,
|
||||
.cra_init = nx842_crypto_init,
|
||||
.cra_exit = nx842_crypto_exit,
|
||||
.cra_u = { .compress = {
|
||||
.coa_compress = nx842_crypto_compress,
|
||||
.coa_decompress = nx842_crypto_decompress } }
|
||||
};
|
||||
|
||||
static int __init nx842_crypto_mod_init(void)
|
||||
{
|
||||
return crypto_register_alg(&alg);
|
||||
}
|
||||
module_init(nx842_crypto_mod_init);
|
||||
|
||||
static void __exit nx842_crypto_mod_exit(void)
|
||||
{
|
||||
crypto_unregister_alg(&alg);
|
||||
}
|
||||
module_exit(nx842_crypto_mod_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("IBM PowerPC Nest (NX) 842 Hardware Compression Interface");
|
||||
MODULE_ALIAS_CRYPTO("842");
|
||||
MODULE_ALIAS_CRYPTO("842-nx");
|
||||
MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
|
84
drivers/crypto/nx/nx-842-platform.c
Normal file
84
drivers/crypto/nx/nx-842-platform.c
Normal file
@ -0,0 +1,84 @@
|
||||
|
||||
#include "nx-842.h"
|
||||
|
||||
/* this is needed, separate from the main nx-842.c driver, because that main
|
||||
* driver loads the platform drivers during its init(), and it expects one
|
||||
* (or none) of the platform drivers to set this pointer to its driver.
|
||||
* That means this pointer can't be in the main nx-842 driver, because it
|
||||
* wouldn't be accessible until after the main driver loaded, which wouldn't
|
||||
* be possible as it's waiting for the platform driver to load. So place it
|
||||
* here.
|
||||
*/
|
||||
static struct nx842_driver *driver;
|
||||
static DEFINE_SPINLOCK(driver_lock);
|
||||
|
||||
struct nx842_driver *nx842_platform_driver(void)
|
||||
{
|
||||
return driver;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nx842_platform_driver);
|
||||
|
||||
bool nx842_platform_driver_set(struct nx842_driver *_driver)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
spin_lock(&driver_lock);
|
||||
|
||||
if (!driver) {
|
||||
driver = _driver;
|
||||
ret = true;
|
||||
} else
|
||||
WARN(1, "can't set platform driver, already set to %s\n",
|
||||
driver->name);
|
||||
|
||||
spin_unlock(&driver_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nx842_platform_driver_set);
|
||||
|
||||
/* only call this from the platform driver exit function */
|
||||
void nx842_platform_driver_unset(struct nx842_driver *_driver)
|
||||
{
|
||||
spin_lock(&driver_lock);
|
||||
|
||||
if (driver == _driver)
|
||||
driver = NULL;
|
||||
else if (driver)
|
||||
WARN(1, "can't unset platform driver %s, currently set to %s\n",
|
||||
_driver->name, driver->name);
|
||||
else
|
||||
WARN(1, "can't unset platform driver, already unset\n");
|
||||
|
||||
spin_unlock(&driver_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nx842_platform_driver_unset);
|
||||
|
||||
bool nx842_platform_driver_get(void)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
spin_lock(&driver_lock);
|
||||
|
||||
if (driver)
|
||||
ret = try_module_get(driver->owner);
|
||||
|
||||
spin_unlock(&driver_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nx842_platform_driver_get);
|
||||
|
||||
void nx842_platform_driver_put(void)
|
||||
{
|
||||
spin_lock(&driver_lock);
|
||||
|
||||
if (driver)
|
||||
module_put(driver->owner);
|
||||
|
||||
spin_unlock(&driver_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nx842_platform_driver_put);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
|
||||
MODULE_DESCRIPTION("842 H/W Compression platform driver");
|
637
drivers/crypto/nx/nx-842-powernv.c
Normal file
637
drivers/crypto/nx/nx-842-powernv.c
Normal file
@ -0,0 +1,637 @@
|
||||
/*
|
||||
* Driver for IBM PowerNV 842 compression accelerator
|
||||
*
|
||||
* Copyright (C) 2015 Dan Streetman, IBM Corp
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include "nx-842.h"
|
||||
|
||||
#include <linux/timer.h>
|
||||
|
||||
#include <asm/prom.h>
|
||||
#include <asm/icswx.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
|
||||
MODULE_DESCRIPTION("842 H/W Compression driver for IBM PowerNV processors");
|
||||
|
||||
#define WORKMEM_ALIGN (CRB_ALIGN)
|
||||
#define CSB_WAIT_MAX (5000) /* ms */
|
||||
|
||||
struct nx842_workmem {
|
||||
/* Below fields must be properly aligned */
|
||||
struct coprocessor_request_block crb; /* CRB_ALIGN align */
|
||||
struct data_descriptor_entry ddl_in[DDL_LEN_MAX]; /* DDE_ALIGN align */
|
||||
struct data_descriptor_entry ddl_out[DDL_LEN_MAX]; /* DDE_ALIGN align */
|
||||
/* Above fields must be properly aligned */
|
||||
|
||||
ktime_t start;
|
||||
|
||||
char padding[WORKMEM_ALIGN]; /* unused, to allow alignment */
|
||||
} __packed __aligned(WORKMEM_ALIGN);
|
||||
|
||||
struct nx842_coproc {
|
||||
unsigned int chip_id;
|
||||
unsigned int ct;
|
||||
unsigned int ci;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
/* no cpu hotplug on powernv, so this list never changes after init */
|
||||
static LIST_HEAD(nx842_coprocs);
|
||||
static unsigned int nx842_ct;
|
||||
|
||||
/**
|
||||
* setup_indirect_dde - Setup an indirect DDE
|
||||
*
|
||||
* The DDE is setup with the the DDE count, byte count, and address of
|
||||
* first direct DDE in the list.
|
||||
*/
|
||||
static void setup_indirect_dde(struct data_descriptor_entry *dde,
|
||||
struct data_descriptor_entry *ddl,
|
||||
unsigned int dde_count, unsigned int byte_count)
|
||||
{
|
||||
dde->flags = 0;
|
||||
dde->count = dde_count;
|
||||
dde->index = 0;
|
||||
dde->length = cpu_to_be32(byte_count);
|
||||
dde->address = cpu_to_be64(nx842_get_pa(ddl));
|
||||
}
|
||||
|
||||
/**
|
||||
* setup_direct_dde - Setup single DDE from buffer
|
||||
*
|
||||
* The DDE is setup with the buffer and length. The buffer must be properly
|
||||
* aligned. The used length is returned.
|
||||
* Returns:
|
||||
* N Successfully set up DDE with N bytes
|
||||
*/
|
||||
static unsigned int setup_direct_dde(struct data_descriptor_entry *dde,
|
||||
unsigned long pa, unsigned int len)
|
||||
{
|
||||
unsigned int l = min_t(unsigned int, len, LEN_ON_PAGE(pa));
|
||||
|
||||
dde->flags = 0;
|
||||
dde->count = 0;
|
||||
dde->index = 0;
|
||||
dde->length = cpu_to_be32(l);
|
||||
dde->address = cpu_to_be64(pa);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
/**
|
||||
* setup_ddl - Setup DDL from buffer
|
||||
*
|
||||
* Returns:
|
||||
* 0 Successfully set up DDL
|
||||
*/
|
||||
static int setup_ddl(struct data_descriptor_entry *dde,
|
||||
struct data_descriptor_entry *ddl,
|
||||
unsigned char *buf, unsigned int len,
|
||||
bool in)
|
||||
{
|
||||
unsigned long pa = nx842_get_pa(buf);
|
||||
int i, ret, total_len = len;
|
||||
|
||||
if (!IS_ALIGNED(pa, DDE_BUFFER_ALIGN)) {
|
||||
pr_debug("%s buffer pa 0x%lx not 0x%x-byte aligned\n",
|
||||
in ? "input" : "output", pa, DDE_BUFFER_ALIGN);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* only need to check last mult; since buffer must be
|
||||
* DDE_BUFFER_ALIGN aligned, and that is a multiple of
|
||||
* DDE_BUFFER_SIZE_MULT, and pre-last page DDE buffers
|
||||
* are guaranteed a multiple of DDE_BUFFER_SIZE_MULT.
|
||||
*/
|
||||
if (len % DDE_BUFFER_LAST_MULT) {
|
||||
pr_debug("%s buffer len 0x%x not a multiple of 0x%x\n",
|
||||
in ? "input" : "output", len, DDE_BUFFER_LAST_MULT);
|
||||
if (in)
|
||||
return -EINVAL;
|
||||
len = round_down(len, DDE_BUFFER_LAST_MULT);
|
||||
}
|
||||
|
||||
/* use a single direct DDE */
|
||||
if (len <= LEN_ON_PAGE(pa)) {
|
||||
ret = setup_direct_dde(dde, pa, len);
|
||||
WARN_ON(ret < len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* use the DDL */
|
||||
for (i = 0; i < DDL_LEN_MAX && len > 0; i++) {
|
||||
ret = setup_direct_dde(&ddl[i], pa, len);
|
||||
buf += ret;
|
||||
len -= ret;
|
||||
pa = nx842_get_pa(buf);
|
||||
}
|
||||
|
||||
if (len > 0) {
|
||||
pr_debug("0x%x total %s bytes 0x%x too many for DDL.\n",
|
||||
total_len, in ? "input" : "output", len);
|
||||
if (in)
|
||||
return -EMSGSIZE;
|
||||
total_len -= len;
|
||||
}
|
||||
setup_indirect_dde(dde, ddl, i, total_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define CSB_ERR(csb, msg, ...) \
|
||||
pr_err("ERROR: " msg " : %02x %02x %02x %02x %08x\n", \
|
||||
##__VA_ARGS__, (csb)->flags, \
|
||||
(csb)->cs, (csb)->cc, (csb)->ce, \
|
||||
be32_to_cpu((csb)->count))
|
||||
|
||||
#define CSB_ERR_ADDR(csb, msg, ...) \
|
||||
CSB_ERR(csb, msg " at %lx", ##__VA_ARGS__, \
|
||||
(unsigned long)be64_to_cpu((csb)->address))
|
||||
|
||||
/**
|
||||
* wait_for_csb
|
||||
*/
|
||||
static int wait_for_csb(struct nx842_workmem *wmem,
|
||||
struct coprocessor_status_block *csb)
|
||||
{
|
||||
ktime_t start = wmem->start, now = ktime_get();
|
||||
ktime_t timeout = ktime_add_ms(start, CSB_WAIT_MAX);
|
||||
|
||||
while (!(ACCESS_ONCE(csb->flags) & CSB_V)) {
|
||||
cpu_relax();
|
||||
now = ktime_get();
|
||||
if (ktime_after(now, timeout))
|
||||
break;
|
||||
}
|
||||
|
||||
/* hw has updated csb and output buffer */
|
||||
barrier();
|
||||
|
||||
/* check CSB flags */
|
||||
if (!(csb->flags & CSB_V)) {
|
||||
CSB_ERR(csb, "CSB still not valid after %ld us, giving up",
|
||||
(long)ktime_us_delta(now, start));
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
if (csb->flags & CSB_F) {
|
||||
CSB_ERR(csb, "Invalid CSB format");
|
||||
return -EPROTO;
|
||||
}
|
||||
if (csb->flags & CSB_CH) {
|
||||
CSB_ERR(csb, "Invalid CSB chaining state");
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
/* verify CSB completion sequence is 0 */
|
||||
if (csb->cs) {
|
||||
CSB_ERR(csb, "Invalid CSB completion sequence");
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
/* check CSB Completion Code */
|
||||
switch (csb->cc) {
|
||||
/* no error */
|
||||
case CSB_CC_SUCCESS:
|
||||
break;
|
||||
case CSB_CC_TPBC_GT_SPBC:
|
||||
/* not an error, but the compressed data is
|
||||
* larger than the uncompressed data :(
|
||||
*/
|
||||
break;
|
||||
|
||||
/* input data errors */
|
||||
case CSB_CC_OPERAND_OVERLAP:
|
||||
/* input and output buffers overlap */
|
||||
CSB_ERR(csb, "Operand Overlap error");
|
||||
return -EINVAL;
|
||||
case CSB_CC_INVALID_OPERAND:
|
||||
CSB_ERR(csb, "Invalid operand");
|
||||
return -EINVAL;
|
||||
case CSB_CC_NOSPC:
|
||||
/* output buffer too small */
|
||||
return -ENOSPC;
|
||||
case CSB_CC_ABORT:
|
||||
CSB_ERR(csb, "Function aborted");
|
||||
return -EINTR;
|
||||
case CSB_CC_CRC_MISMATCH:
|
||||
CSB_ERR(csb, "CRC mismatch");
|
||||
return -EINVAL;
|
||||
case CSB_CC_TEMPL_INVALID:
|
||||
CSB_ERR(csb, "Compressed data template invalid");
|
||||
return -EINVAL;
|
||||
case CSB_CC_TEMPL_OVERFLOW:
|
||||
CSB_ERR(csb, "Compressed data template shows data past end");
|
||||
return -EINVAL;
|
||||
|
||||
/* these should not happen */
|
||||
case CSB_CC_INVALID_ALIGN:
|
||||
/* setup_ddl should have detected this */
|
||||
CSB_ERR_ADDR(csb, "Invalid alignment");
|
||||
return -EINVAL;
|
||||
case CSB_CC_DATA_LENGTH:
|
||||
/* setup_ddl should have detected this */
|
||||
CSB_ERR(csb, "Invalid data length");
|
||||
return -EINVAL;
|
||||
case CSB_CC_WR_TRANSLATION:
|
||||
case CSB_CC_TRANSLATION:
|
||||
case CSB_CC_TRANSLATION_DUP1:
|
||||
case CSB_CC_TRANSLATION_DUP2:
|
||||
case CSB_CC_TRANSLATION_DUP3:
|
||||
case CSB_CC_TRANSLATION_DUP4:
|
||||
case CSB_CC_TRANSLATION_DUP5:
|
||||
case CSB_CC_TRANSLATION_DUP6:
|
||||
/* should not happen, we use physical addrs */
|
||||
CSB_ERR_ADDR(csb, "Translation error");
|
||||
return -EPROTO;
|
||||
case CSB_CC_WR_PROTECTION:
|
||||
case CSB_CC_PROTECTION:
|
||||
case CSB_CC_PROTECTION_DUP1:
|
||||
case CSB_CC_PROTECTION_DUP2:
|
||||
case CSB_CC_PROTECTION_DUP3:
|
||||
case CSB_CC_PROTECTION_DUP4:
|
||||
case CSB_CC_PROTECTION_DUP5:
|
||||
case CSB_CC_PROTECTION_DUP6:
|
||||
/* should not happen, we use physical addrs */
|
||||
CSB_ERR_ADDR(csb, "Protection error");
|
||||
return -EPROTO;
|
||||
case CSB_CC_PRIVILEGE:
|
||||
/* shouldn't happen, we're in HYP mode */
|
||||
CSB_ERR(csb, "Insufficient Privilege error");
|
||||
return -EPROTO;
|
||||
case CSB_CC_EXCESSIVE_DDE:
|
||||
/* shouldn't happen, setup_ddl doesn't use many dde's */
|
||||
CSB_ERR(csb, "Too many DDEs in DDL");
|
||||
return -EINVAL;
|
||||
case CSB_CC_TRANSPORT:
|
||||
/* shouldn't happen, we setup CRB correctly */
|
||||
CSB_ERR(csb, "Invalid CRB");
|
||||
return -EINVAL;
|
||||
case CSB_CC_SEGMENTED_DDL:
|
||||
/* shouldn't happen, setup_ddl creates DDL right */
|
||||
CSB_ERR(csb, "Segmented DDL error");
|
||||
return -EINVAL;
|
||||
case CSB_CC_DDE_OVERFLOW:
|
||||
/* shouldn't happen, setup_ddl creates DDL right */
|
||||
CSB_ERR(csb, "DDE overflow error");
|
||||
return -EINVAL;
|
||||
case CSB_CC_SESSION:
|
||||
/* should not happen with ICSWX */
|
||||
CSB_ERR(csb, "Session violation error");
|
||||
return -EPROTO;
|
||||
case CSB_CC_CHAIN:
|
||||
/* should not happen, we don't use chained CRBs */
|
||||
CSB_ERR(csb, "Chained CRB error");
|
||||
return -EPROTO;
|
||||
case CSB_CC_SEQUENCE:
|
||||
/* should not happen, we don't use chained CRBs */
|
||||
CSB_ERR(csb, "CRB seqeunce number error");
|
||||
return -EPROTO;
|
||||
case CSB_CC_UNKNOWN_CODE:
|
||||
CSB_ERR(csb, "Unknown subfunction code");
|
||||
return -EPROTO;
|
||||
|
||||
/* hardware errors */
|
||||
case CSB_CC_RD_EXTERNAL:
|
||||
case CSB_CC_RD_EXTERNAL_DUP1:
|
||||
case CSB_CC_RD_EXTERNAL_DUP2:
|
||||
case CSB_CC_RD_EXTERNAL_DUP3:
|
||||
CSB_ERR_ADDR(csb, "Read error outside coprocessor");
|
||||
return -EPROTO;
|
||||
case CSB_CC_WR_EXTERNAL:
|
||||
CSB_ERR_ADDR(csb, "Write error outside coprocessor");
|
||||
return -EPROTO;
|
||||
case CSB_CC_INTERNAL:
|
||||
CSB_ERR(csb, "Internal error in coprocessor");
|
||||
return -EPROTO;
|
||||
case CSB_CC_PROVISION:
|
||||
CSB_ERR(csb, "Storage provision error");
|
||||
return -EPROTO;
|
||||
case CSB_CC_HW:
|
||||
CSB_ERR(csb, "Correctable hardware error");
|
||||
return -EPROTO;
|
||||
|
||||
default:
|
||||
CSB_ERR(csb, "Invalid CC %d", csb->cc);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
/* check Completion Extension state */
|
||||
if (csb->ce & CSB_CE_TERMINATION) {
|
||||
CSB_ERR(csb, "CSB request was terminated");
|
||||
return -EPROTO;
|
||||
}
|
||||
if (csb->ce & CSB_CE_INCOMPLETE) {
|
||||
CSB_ERR(csb, "CSB request not complete");
|
||||
return -EPROTO;
|
||||
}
|
||||
if (!(csb->ce & CSB_CE_TPBC)) {
|
||||
CSB_ERR(csb, "TPBC not provided, unknown target length");
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
/* successful completion */
|
||||
pr_debug_ratelimited("Processed %u bytes in %lu us\n", csb->count,
|
||||
(unsigned long)ktime_us_delta(now, start));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* nx842_powernv_function - compress/decompress data using the 842 algorithm
|
||||
*
|
||||
* (De)compression provided by the NX842 coprocessor on IBM PowerNV systems.
|
||||
* This compresses or decompresses the provided input buffer into the provided
|
||||
* output buffer.
|
||||
*
|
||||
* Upon return from this function @outlen contains the length of the
|
||||
* output data. If there is an error then @outlen will be 0 and an
|
||||
* error will be specified by the return code from this function.
|
||||
*
|
||||
* The @workmem buffer should only be used by one function call at a time.
|
||||
*
|
||||
* @in: input buffer pointer
|
||||
* @inlen: input buffer size
|
||||
* @out: output buffer pointer
|
||||
* @outlenp: output buffer size pointer
|
||||
* @workmem: working memory buffer pointer, size determined by
|
||||
* nx842_powernv_driver.workmem_size
|
||||
* @fc: function code, see CCW Function Codes in nx-842.h
|
||||
*
|
||||
* Returns:
|
||||
* 0 Success, output of length @outlenp stored in the buffer at @out
|
||||
* -ENODEV Hardware unavailable
|
||||
* -ENOSPC Output buffer is to small
|
||||
* -EMSGSIZE Input buffer too large
|
||||
* -EINVAL buffer constraints do not fix nx842_constraints
|
||||
* -EPROTO hardware error during operation
|
||||
* -ETIMEDOUT hardware did not complete operation in reasonable time
|
||||
* -EINTR operation was aborted
|
||||
*/
|
||||
static int nx842_powernv_function(const unsigned char *in, unsigned int inlen,
|
||||
unsigned char *out, unsigned int *outlenp,
|
||||
void *workmem, int fc)
|
||||
{
|
||||
struct coprocessor_request_block *crb;
|
||||
struct coprocessor_status_block *csb;
|
||||
struct nx842_workmem *wmem;
|
||||
int ret;
|
||||
u64 csb_addr;
|
||||
u32 ccw;
|
||||
unsigned int outlen = *outlenp;
|
||||
|
||||
wmem = PTR_ALIGN(workmem, WORKMEM_ALIGN);
|
||||
|
||||
*outlenp = 0;
|
||||
|
||||
/* shoudn't happen, we don't load without a coproc */
|
||||
if (!nx842_ct) {
|
||||
pr_err_ratelimited("coprocessor CT is 0");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
crb = &wmem->crb;
|
||||
csb = &crb->csb;
|
||||
|
||||
/* Clear any previous values */
|
||||
memset(crb, 0, sizeof(*crb));
|
||||
|
||||
/* set up DDLs */
|
||||
ret = setup_ddl(&crb->source, wmem->ddl_in,
|
||||
(unsigned char *)in, inlen, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = setup_ddl(&crb->target, wmem->ddl_out,
|
||||
out, outlen, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* set up CCW */
|
||||
ccw = 0;
|
||||
ccw = SET_FIELD(ccw, CCW_CT, nx842_ct);
|
||||
ccw = SET_FIELD(ccw, CCW_CI_842, 0); /* use 0 for hw auto-selection */
|
||||
ccw = SET_FIELD(ccw, CCW_FC_842, fc);
|
||||
|
||||
/* set up CRB's CSB addr */
|
||||
csb_addr = nx842_get_pa(csb) & CRB_CSB_ADDRESS;
|
||||
csb_addr |= CRB_CSB_AT; /* Addrs are phys */
|
||||
crb->csb_addr = cpu_to_be64(csb_addr);
|
||||
|
||||
wmem->start = ktime_get();
|
||||
|
||||
/* do ICSWX */
|
||||
ret = icswx(cpu_to_be32(ccw), crb);
|
||||
|
||||
pr_debug_ratelimited("icswx CR %x ccw %x crb->ccw %x\n", ret,
|
||||
(unsigned int)ccw,
|
||||
(unsigned int)be32_to_cpu(crb->ccw));
|
||||
|
||||
switch (ret) {
|
||||
case ICSWX_INITIATED:
|
||||
ret = wait_for_csb(wmem, csb);
|
||||
break;
|
||||
case ICSWX_BUSY:
|
||||
pr_debug_ratelimited("842 Coprocessor busy\n");
|
||||
ret = -EBUSY;
|
||||
break;
|
||||
case ICSWX_REJECTED:
|
||||
pr_err_ratelimited("ICSWX rejected\n");
|
||||
ret = -EPROTO;
|
||||
break;
|
||||
default:
|
||||
pr_err_ratelimited("Invalid ICSWX return code %x\n", ret);
|
||||
ret = -EPROTO;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
*outlenp = be32_to_cpu(csb->count);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* nx842_powernv_compress - Compress data using the 842 algorithm
|
||||
*
|
||||
* Compression provided by the NX842 coprocessor on IBM PowerNV systems.
|
||||
* The input buffer is compressed and the result is stored in the
|
||||
* provided output buffer.
|
||||
*
|
||||
* Upon return from this function @outlen contains the length of the
|
||||
* compressed data. If there is an error then @outlen will be 0 and an
|
||||
* error will be specified by the return code from this function.
|
||||
*
|
||||
* @in: input buffer pointer
|
||||
* @inlen: input buffer size
|
||||
* @out: output buffer pointer
|
||||
* @outlenp: output buffer size pointer
|
||||
* @workmem: working memory buffer pointer, size determined by
|
||||
* nx842_powernv_driver.workmem_size
|
||||
*
|
||||
* Returns: see @nx842_powernv_function()
|
||||
*/
|
||||
static int nx842_powernv_compress(const unsigned char *in, unsigned int inlen,
|
||||
unsigned char *out, unsigned int *outlenp,
|
||||
void *wmem)
|
||||
{
|
||||
return nx842_powernv_function(in, inlen, out, outlenp,
|
||||
wmem, CCW_FC_842_COMP_NOCRC);
|
||||
}
|
||||
|
||||
/**
|
||||
* nx842_powernv_decompress - Decompress data using the 842 algorithm
|
||||
*
|
||||
* Decompression provided by the NX842 coprocessor on IBM PowerNV systems.
|
||||
* The input buffer is decompressed and the result is stored in the
|
||||
* provided output buffer.
|
||||
*
|
||||
* Upon return from this function @outlen contains the length of the
|
||||
* decompressed data. If there is an error then @outlen will be 0 and an
|
||||
* error will be specified by the return code from this function.
|
||||
*
|
||||
* @in: input buffer pointer
|
||||
* @inlen: input buffer size
|
||||
* @out: output buffer pointer
|
||||
* @outlenp: output buffer size pointer
|
||||
* @workmem: working memory buffer pointer, size determined by
|
||||
* nx842_powernv_driver.workmem_size
|
||||
*
|
||||
* Returns: see @nx842_powernv_function()
|
||||
*/
|
||||
static int nx842_powernv_decompress(const unsigned char *in, unsigned int inlen,
|
||||
unsigned char *out, unsigned int *outlenp,
|
||||
void *wmem)
|
||||
{
|
||||
return nx842_powernv_function(in, inlen, out, outlenp,
|
||||
wmem, CCW_FC_842_DECOMP_NOCRC);
|
||||
}
|
||||
|
||||
static int __init nx842_powernv_probe(struct device_node *dn)
|
||||
{
|
||||
struct nx842_coproc *coproc;
|
||||
struct property *ct_prop, *ci_prop;
|
||||
unsigned int ct, ci;
|
||||
int chip_id;
|
||||
|
||||
chip_id = of_get_ibm_chip_id(dn);
|
||||
if (chip_id < 0) {
|
||||
pr_err("ibm,chip-id missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ct_prop = of_find_property(dn, "ibm,842-coprocessor-type", NULL);
|
||||
if (!ct_prop) {
|
||||
pr_err("ibm,842-coprocessor-type missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ct = be32_to_cpu(*(unsigned int *)ct_prop->value);
|
||||
ci_prop = of_find_property(dn, "ibm,842-coprocessor-instance", NULL);
|
||||
if (!ci_prop) {
|
||||
pr_err("ibm,842-coprocessor-instance missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ci = be32_to_cpu(*(unsigned int *)ci_prop->value);
|
||||
|
||||
coproc = kmalloc(sizeof(*coproc), GFP_KERNEL);
|
||||
if (!coproc)
|
||||
return -ENOMEM;
|
||||
|
||||
coproc->chip_id = chip_id;
|
||||
coproc->ct = ct;
|
||||
coproc->ci = ci;
|
||||
INIT_LIST_HEAD(&coproc->list);
|
||||
list_add(&coproc->list, &nx842_coprocs);
|
||||
|
||||
pr_info("coprocessor found on chip %d, CT %d CI %d\n", chip_id, ct, ci);
|
||||
|
||||
if (!nx842_ct)
|
||||
nx842_ct = ct;
|
||||
else if (nx842_ct != ct)
|
||||
pr_err("NX842 chip %d, CT %d != first found CT %d\n",
|
||||
chip_id, ct, nx842_ct);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct nx842_constraints nx842_powernv_constraints = {
|
||||
.alignment = DDE_BUFFER_ALIGN,
|
||||
.multiple = DDE_BUFFER_LAST_MULT,
|
||||
.minimum = DDE_BUFFER_LAST_MULT,
|
||||
.maximum = (DDL_LEN_MAX - 1) * PAGE_SIZE,
|
||||
};
|
||||
|
||||
static struct nx842_driver nx842_powernv_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.owner = THIS_MODULE,
|
||||
.workmem_size = sizeof(struct nx842_workmem),
|
||||
.constraints = &nx842_powernv_constraints,
|
||||
.compress = nx842_powernv_compress,
|
||||
.decompress = nx842_powernv_decompress,
|
||||
};
|
||||
|
||||
static __init int nx842_powernv_init(void)
|
||||
{
|
||||
struct device_node *dn;
|
||||
|
||||
/* verify workmem size/align restrictions */
|
||||
BUILD_BUG_ON(WORKMEM_ALIGN % CRB_ALIGN);
|
||||
BUILD_BUG_ON(CRB_ALIGN % DDE_ALIGN);
|
||||
BUILD_BUG_ON(CRB_SIZE % DDE_ALIGN);
|
||||
/* verify buffer size/align restrictions */
|
||||
BUILD_BUG_ON(PAGE_SIZE % DDE_BUFFER_ALIGN);
|
||||
BUILD_BUG_ON(DDE_BUFFER_ALIGN % DDE_BUFFER_SIZE_MULT);
|
||||
BUILD_BUG_ON(DDE_BUFFER_SIZE_MULT % DDE_BUFFER_LAST_MULT);
|
||||
|
||||
pr_info("loading\n");
|
||||
|
||||
for_each_compatible_node(dn, NULL, "ibm,power-nx")
|
||||
nx842_powernv_probe(dn);
|
||||
|
||||
if (!nx842_ct) {
|
||||
pr_err("no coprocessors found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!nx842_platform_driver_set(&nx842_powernv_driver)) {
|
||||
struct nx842_coproc *coproc, *n;
|
||||
|
||||
list_for_each_entry_safe(coproc, n, &nx842_coprocs, list) {
|
||||
list_del(&coproc->list);
|
||||
kfree(coproc);
|
||||
}
|
||||
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
pr_info("loaded\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(nx842_powernv_init);
|
||||
|
||||
static void __exit nx842_powernv_exit(void)
|
||||
{
|
||||
struct nx842_coproc *coproc, *n;
|
||||
|
||||
nx842_platform_driver_unset(&nx842_powernv_driver);
|
||||
|
||||
list_for_each_entry_safe(coproc, n, &nx842_coprocs, list) {
|
||||
list_del(&coproc->list);
|
||||
kfree(coproc);
|
||||
}
|
||||
|
||||
pr_info("unloaded\n");
|
||||
}
|
||||
module_exit(nx842_powernv_exit);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user