switch (hash_alg) { case HASH_ALG_MD5:
hash_key_len = 16;
name = "MD5"; break; case HASH_ALG_SHA1:
hash_key_len = 20;
name = "SHA1"; break; case HASH_ALG_SHA224:
hash_key_len = 28;
name = "SHA224"; break; case HASH_ALG_SHA256:
hash_key_len = 32;
name = "SHA256"; break; case HASH_ALG_SHA384:
hash_key_len = 48;
name = "SHA384"; break; case HASH_ALG_SHA512:
hash_key_len = 64;
name = "SHA512"; break; case HASH_ALG_AES:
hash_key_len = 0;
name = "AES"; break; case HASH_ALG_NONE: break;
}
switch (hash_alg) { case HASH_ALG_MD5:
hash_state_len = 16;
name = "MD5"; break; case HASH_ALG_SHA1:
hash_state_len = 20;
name = "SHA1"; break; case HASH_ALG_SHA224:
hash_state_len = 32;
name = "SHA224"; break; case HASH_ALG_SHA256:
hash_state_len = 32;
name = "SHA256"; break; case HASH_ALG_SHA384:
hash_state_len = 48;
name = "SHA384"; break; case HASH_ALG_SHA512:
hash_state_len = 64;
name = "SHA512"; break; case HASH_ALG_AES:
hash_state_len = 0;
name = "AES"; break; case HASH_ALG_NONE: break;
}
switch (cipher_alg) { case CIPHER_ALG_DES:
cipher_key_len = 8;
name = "DES"; break; case CIPHER_ALG_3DES:
cipher_key_len = 24;
name = "3DES"; break; case CIPHER_ALG_AES: switch (cipher_type) { case CIPHER_TYPE_AES128:
cipher_key_len = 16;
name = "AES128"; break; case CIPHER_TYPE_AES192:
cipher_key_len = 24;
name = "AES192"; break; case CIPHER_TYPE_AES256:
cipher_key_len = 32;
name = "AES256"; break;
} break; case CIPHER_ALG_NONE: break;
}
/** * spum_ns2_ctx_max_payload() - Determine the max length of the payload for a * SPU message for a given cipher and hash alg context. * @cipher_alg: The cipher algorithm * @cipher_mode: The cipher mode * @blocksize: The size of a block of data for this algo * * The max payload must be a multiple of the blocksize so that if a request is * too large to fit in a single SPU message, the request can be broken into * max_payload sized chunks. Each chunk must be a multiple of blocksize. * * Return: Max payload length in bytes
*/
u32 spum_ns2_ctx_max_payload(enum spu_cipher_alg cipher_alg, enum spu_cipher_mode cipher_mode, unsignedint blocksize)
{
u32 max_payload = SPUM_NS2_MAX_PAYLOAD;
u32 excess;
/* In XTS on SPU-M, we'll need to insert tweak before input data */ if (cipher_mode == CIPHER_MODE_XTS)
max_payload -= SPU_XTS_TWEAK_SIZE;
excess = max_payload % blocksize;
return max_payload - excess;
}
/** * spum_nsp_ctx_max_payload() - Determine the max length of the payload for a * SPU message for a given cipher and hash alg context. * @cipher_alg: The cipher algorithm * @cipher_mode: The cipher mode * @blocksize: The size of a block of data for this algo * * The max payload must be a multiple of the blocksize so that if a request is * too large to fit in a single SPU message, the request can be broken into * max_payload sized chunks. Each chunk must be a multiple of blocksize. * * Return: Max payload length in bytes
*/
u32 spum_nsp_ctx_max_payload(enum spu_cipher_alg cipher_alg, enum spu_cipher_mode cipher_mode, unsignedint blocksize)
{
u32 max_payload = SPUM_NSP_MAX_PAYLOAD;
u32 excess;
/* In XTS on SPU-M, we'll need to insert tweak before input data */ if (cipher_mode == CIPHER_MODE_XTS)
max_payload -= SPU_XTS_TWEAK_SIZE;
excess = max_payload % blocksize;
return max_payload - excess;
}
/** spum_payload_length() - Given a SPU-M message header, extract the payload * length. * @spu_hdr: Start of SPU header * * Assumes just MH, EMH, BD (no SCTX, BDESC. Works for response frames. * * Return: payload length in bytes
*/
u32 spum_payload_length(u8 *spu_hdr)
{ struct BD_HEADER *bd;
u32 pl_len;
/** * spum_response_hdr_len() - Given the length of the hash key and encryption * key, determine the expected length of a SPU response header. * @auth_key_len: authentication key length (bytes) * @enc_key_len: encryption key length (bytes) * @is_hash: true if response message is for a hash operation * * Return: length of SPU response header (bytes)
*/
u16 spum_response_hdr_len(u16 auth_key_len, u16 enc_key_len, bool is_hash)
{ if (is_hash) return SPU_HASH_RESP_HDR_LEN; else return SPU_RESP_HDR_LEN;
}
/** * spum_hash_pad_len() - Calculate the length of hash padding required to extend * data to a full block size. * @hash_alg: hash algorithm * @hash_mode: hash mode * @chunksize: length of data, in bytes * @hash_block_size: size of a block of data for hash algorithm * * Reserve space for 1 byte (0x80) start of pad and the total length as u64 * * Return: length of hash pad in bytes
*/
u16 spum_hash_pad_len(enum hash_alg hash_alg, enum hash_mode hash_mode,
u32 chunksize, u16 hash_block_size)
{ unsignedint length_len; unsignedint used_space_last_block; int hash_pad_len;
/* AES-XCBC hash requires just padding to next block boundary */ if ((hash_alg == HASH_ALG_AES) && (hash_mode == HASH_MODE_XCBC)) {
used_space_last_block = chunksize % hash_block_size;
hash_pad_len = hash_block_size - used_space_last_block; if (hash_pad_len >= hash_block_size)
hash_pad_len -= hash_block_size; return hash_pad_len;
}
/** * spum_assoc_resp_len() - Determine the size of the receive buffer required to * catch associated data. * @cipher_mode: cipher mode * @assoc_len: length of associated data (bytes) * @iv_len: length of IV (bytes) * @is_encrypt: true if encrypting. false if decrypting. * * Return: length of associated data in response message (bytes)
*/
u32 spum_assoc_resp_len(enum spu_cipher_mode cipher_mode, unsignedint assoc_len, unsignedint iv_len, bool is_encrypt)
{
u32 buflen = 0;
u32 pad;
if (assoc_len)
buflen = assoc_len;
if (cipher_mode == CIPHER_MODE_GCM) { /* AAD needs to be padded in responses too */
pad = spum_gcm_ccm_pad_len(cipher_mode, buflen);
buflen += pad;
} if (cipher_mode == CIPHER_MODE_CCM) { /* * AAD needs to be padded in responses too * for CCM, len + 2 needs to be 128-bit aligned.
*/
pad = spum_gcm_ccm_pad_len(cipher_mode, buflen + 2);
buflen += pad;
}
return buflen;
}
/** * spum_aead_ivlen() - Calculate the length of the AEAD IV to be included * in a SPU request after the AAD and before the payload. * @cipher_mode: cipher mode * @iv_len: initialization vector length in bytes * * In Linux ~4.2 and later, the assoc_data sg includes the IV. So no need * to include the IV as a separate field in the SPU request msg. * * Return: Length of AEAD IV in bytes
*/
u8 spum_aead_ivlen(enum spu_cipher_mode cipher_mode, u16 iv_len)
{ return 0;
}
/** * spum_hash_type() - Determine the type of hash operation. * @src_sent: The number of bytes in the current request that have already * been sent to the SPU to be hashed. * * We do not use HASH_TYPE_FULL for requests that fit in a single SPU message. * Using FULL causes failures (such as when the string to be hashed is empty). * For similar reasons, we never use HASH_TYPE_FIN. Instead, submit messages * as INIT or UPDT and do the hash padding in sw.
*/ enum hash_type spum_hash_type(u32 src_sent)
{ return src_sent ? HASH_TYPE_UPDT : HASH_TYPE_INIT;
}
/** * spum_digest_size() - Determine the size of a hash digest to expect the SPU to * return. * @alg_digest_size: Number of bytes in the final digest for the given algo * @alg: The hash algorithm * @htype: Type of hash operation (init, update, full, etc) * * When doing incremental hashing for an algorithm with a truncated hash * (e.g., SHA224), the SPU returns the full digest so that it can be fed back as * a partial result for the next chunk.
*/
u32 spum_digest_size(u32 alg_digest_size, enum hash_alg alg, enum hash_type htype)
{
u32 digestsize = alg_digest_size;
/** * spum_create_request() - Build a SPU request message header, up to and * including the BD header. Construct the message starting at spu_hdr. Caller * should allocate this buffer in DMA-able memory at least SPU_HEADER_ALLOC_LEN * bytes long. * @spu_hdr: Start of buffer where SPU request header is to be written * @req_opts: SPU request message options * @cipher_parms: Parameters related to cipher algorithm * @hash_parms: Parameters related to hash algorithm * @aead_parms: Parameters related to AEAD operation * @data_size: Length of data to be encrypted or authenticated. If AEAD, does * not include length of AAD. * * Return: the length of the SPU header in bytes. 0 if an error occurs.
*/
u32 spum_create_request(u8 *spu_hdr, struct spu_request_opts *req_opts, struct spu_cipher_parms *cipher_parms, struct spu_hash_parms *hash_parms, struct spu_aead_parms *aead_parms, unsignedint data_size)
{ struct SPUHEADER *spuh; struct BDESC_HEADER *bdesc; struct BD_HEADER *bd;
/* size of the cipher payload */ unsignedint cipher_len = hash_parms->prebuf_len + data_size +
hash_parms->pad_len;
/* offset of prebuf or data from end of BD header */ unsignedint cipher_offset = aead_parms->assoc_size +
aead_parms->iv_len + aead_parms->aad_pad_len;
/* total size of the DB data (without STAT word padding) */ unsignedint real_db_size = spu_real_db_size(aead_parms->assoc_size,
aead_parms->iv_len,
hash_parms->prebuf_len,
data_size,
aead_parms->aad_pad_len,
aead_parms->data_pad_len,
hash_parms->pad_len);
/* starting out: zero the header (plus some) */
ptr = spu_hdr;
memset(ptr, 0, sizeof(struct SPUHEADER));
/* format master header word */ /* Do not set the next bit even though the datasheet says to */
spuh = (struct SPUHEADER *)ptr;
ptr += sizeof(struct SPUHEADER);
buf_len += sizeof(struct SPUHEADER);
/* Format sctx word 0 (protocol_bits) */
sctx_words = 3; /* size in words */
/* Format sctx word 1 (cipher_bits) */ if (req_opts->is_inbound)
cipher_bits |= CIPHER_INBOUND; if (req_opts->auth_first)
cipher_bits |= CIPHER_ORDER;
/* Set the crypto parameters in the cipher.flags */
cipher_bits |= cipher_parms->alg << CIPHER_ALG_SHIFT;
cipher_bits |= cipher_parms->mode << CIPHER_MODE_SHIFT;
cipher_bits |= cipher_parms->type << CIPHER_TYPE_SHIFT;
/* Set the auth parameters in the cipher.flags */
cipher_bits |= hash_parms->alg << HASH_ALG_SHIFT;
cipher_bits |= hash_parms->mode << HASH_MODE_SHIFT;
cipher_bits |= hash_parms->type << HASH_TYPE_SHIFT;
/* * Format sctx extensions if required, and update main fields if * required)
*/ if (hash_parms->alg) { /* Write the authentication key material if present */ if (hash_parms->key_len) {
memcpy(ptr, hash_parms->key_buf, hash_parms->key_len);
ptr += hash_parms->key_len;
buf_len += hash_parms->key_len;
sctx_words += hash_parms->key_len / 4;
}
/* if GCM/CCM we need to write ICV into the payload */ if (!req_opts->is_inbound) { if ((cipher_parms->mode == CIPHER_MODE_GCM) ||
(cipher_parms->mode == CIPHER_MODE_CCM))
ecf_bits |= 1 << INSERT_ICV_SHIFT;
} else {
ecf_bits |= CHECK_ICV;
}
/* Inform the SPU of the ICV size (in words) */ if (hash_parms->digestsize == 64)
cipher_bits |= ICV_IS_512; else
ecf_bits |=
(hash_parms->digestsize / 4) << ICV_SIZE_SHIFT;
}
if (req_opts->bd_suppress)
ecf_bits |= BD_SUPPRESS;
/* copy the encryption keys in the SAD entry */ if (cipher_parms->alg) { if (cipher_parms->key_len) {
memcpy(ptr, cipher_parms->key_buf,
cipher_parms->key_len);
ptr += cipher_parms->key_len;
buf_len += cipher_parms->key_len;
sctx_words += cipher_parms->key_len / 4;
}
/* * if encrypting then set IV size, use SCTX IV unless no IV * given here
*/ if (cipher_parms->iv_buf && cipher_parms->iv_len) { /* Use SCTX IV */
ecf_bits |= SCTX_IV;
/* cipher iv provided so put it in here */
memcpy(ptr, cipher_parms->iv_buf, cipher_parms->iv_len);
/* * RFC4543 (GMAC/ESP) requires data to be sent as part of AAD * so we need to override the BDESC parameters.
*/ if (req_opts->is_rfc4543) { if (req_opts->is_inbound)
data_size -= hash_parms->digestsize;
offset_iv = aead_parms->assoc_size + data_size;
cipher_len = 0;
cipher_offset = offset_iv;
auth_len = cipher_offset + aead_parms->data_pad_len;
}
/* write in the total sctx length now that we know it */
protocol_bits |= sctx_words;
/* * CCM in SPU-M requires that ICV not be in same 32-bit word as data or * padding. So account for padding as necessary.
*/ if (cipher_parms->mode == CIPHER_MODE_CCM)
auth_len += spum_wordalign_padlen(auth_len);
/** * spum_cipher_req_init() - Build a SPU request message header, up to and * including the BD header. * @spu_hdr: Start of SPU request header (MH) * @cipher_parms: Parameters that describe the cipher request * * Construct the message starting at spu_hdr. Caller should allocate this buffer * in DMA-able memory at least SPU_HEADER_ALLOC_LEN bytes long. * * Return: the length of the SPU header in bytes. 0 if an error occurs.
*/
u16 spum_cipher_req_init(u8 *spu_hdr, struct spu_cipher_parms *cipher_parms)
{ struct SPUHEADER *spuh;
u32 protocol_bits = 0;
u32 cipher_bits = 0;
u32 ecf_bits = 0;
u8 sctx_words = 0;
/* Format sctx word 0 (protocol_bits) */
sctx_words = 3; /* size in words */
/* copy the encryption keys in the SAD entry */ if (cipher_parms->alg) { if (cipher_parms->key_len)
sctx_words += cipher_parms->key_len / 4;
/* * if encrypting then set IV size, use SCTX IV unless no IV * given here
*/ if (cipher_parms->iv_len) { /* Use SCTX IV */
ecf_bits |= SCTX_IV;
sctx_words += cipher_parms->iv_len / 4;
}
}
/* Set the crypto parameters in the cipher.flags */
cipher_bits |= cipher_parms->alg << CIPHER_ALG_SHIFT;
cipher_bits |= cipher_parms->mode << CIPHER_MODE_SHIFT;
cipher_bits |= cipher_parms->type << CIPHER_TYPE_SHIFT;
/* copy the encryption keys in the SAD entry */ if (cipher_parms->alg && cipher_parms->key_len)
memcpy(spuh + 1, cipher_parms->key_buf, cipher_parms->key_len);
/* write in the total sctx length now that we know it */
protocol_bits |= sctx_words;
/* Endian adjust the SCTX */
spuh->sa.proto_flags = cpu_to_be32(protocol_bits);
/** * spum_cipher_req_finish() - Finish building a SPU request message header for a * block cipher request. Assumes much of the header was already filled in at * setkey() time in spu_cipher_req_init(). * @spu_hdr: Start of the request message header (MH field) * @spu_req_hdr_len: Length in bytes of the SPU request header * @is_inbound: 0 encrypt, 1 decrypt * @cipher_parms: Parameters describing cipher operation to be performed * @data_size: Length of the data in the BD field * * Assumes much of the header was already filled in at setkey() time in * spum_cipher_req_init(). * spum_cipher_req_init() fills in the encryption key.
*/ void spum_cipher_req_finish(u8 *spu_hdr,
u16 spu_req_hdr_len, unsignedint is_inbound, struct spu_cipher_parms *cipher_parms, unsignedint data_size)
{ struct SPUHEADER *spuh; struct BDESC_HEADER *bdesc; struct BD_HEADER *bd;
u8 *bdesc_ptr = spu_hdr + spu_req_hdr_len -
(sizeof(struct BD_HEADER) + sizeof(struct BDESC_HEADER));
/* * In XTS mode, API puts "i" parameter (block tweak) in IV. For * SPU-M, should be in start of the BD; tx_sg_create() copies it there. * IV in SPU msg for SPU-M should be 0, since that's the "j" parameter * (block ctr within larger data unit) - given we can send entire disk * block (<= 4KB) in 1 SPU msg, don't need to use this parameter.
*/ if (cipher_parms->mode == CIPHER_MODE_XTS)
memset(cipher_parms->iv_buf, 0, cipher_parms->iv_len);
/* format master header word */ /* Do not set the next bit even though the datasheet says to */
spuh = (struct SPUHEADER *)spu_hdr;
/* cipher_bits was initialized at setkey time */
cipher_bits = be32_to_cpu(spuh->sa.cipher_flags);
/* Format sctx word 1 (cipher_bits) */ if (is_inbound)
cipher_bits |= CIPHER_INBOUND; else
cipher_bits &= ~CIPHER_INBOUND;
if (cipher_parms->alg && cipher_parms->iv_buf && cipher_parms->iv_len) /* cipher iv provided so put it in here */
memcpy(bdesc_ptr - cipher_parms->iv_len, cipher_parms->iv_buf,
cipher_parms->iv_len);
/** * spum_request_pad() - Create pad bytes at the end of the data. * @pad_start: Start of buffer where pad bytes are to be written * @gcm_ccm_padding: length of GCM/CCM padding, in bytes * @hash_pad_len: Number of bytes of padding extend data to full block * @auth_alg: authentication algorithm * @auth_mode: authentication mode * @total_sent: length inserted at end of hash pad * @status_padding: Number of bytes of padding to align STATUS word * * There may be three forms of pad: * 1. GCM/CCM pad - for GCM/CCM mode ciphers, pad to 16-byte alignment * 2. hash pad - pad to a block length, with 0x80 data terminator and * size at the end * 3. STAT pad - to ensure the STAT field is 4-byte aligned
*/ void spum_request_pad(u8 *pad_start,
u32 gcm_ccm_padding,
u32 hash_pad_len, enum hash_alg auth_alg, enum hash_mode auth_mode, unsignedint total_sent, u32 status_padding)
{
u8 *ptr = pad_start;
/* fix data alignent for GCM/CCM */ if (gcm_ccm_padding > 0) {
flow_log(" GCM: padding to 16 byte alignment: %u bytes\n",
gcm_ccm_padding);
memset(ptr, 0, gcm_ccm_padding);
ptr += gcm_ccm_padding;
}
if (hash_pad_len > 0) { /* clear the padding section */
memset(ptr, 0, hash_pad_len);
if ((auth_alg == HASH_ALG_AES) &&
(auth_mode == HASH_MODE_XCBC)) { /* AES/XCBC just requires padding to be 0s */
ptr += hash_pad_len;
} else { /* terminate the data */
*ptr = 0x80;
ptr += (hash_pad_len - sizeof(u64));
/* add the size at the end as required per alg */ if (auth_alg == HASH_ALG_MD5)
*(__le64 *)ptr = cpu_to_le64(total_sent * 8ull); else/* SHA1, SHA2-224, SHA2-256 */
*(__be64 *)ptr = cpu_to_be64(total_sent * 8ull);
ptr += sizeof(u64);
}
}
/* pad to a 4byte alignment for STAT */ if (status_padding > 0) {
flow_log(" STAT: padding to 4 byte alignment: %u bytes\n",
status_padding);
/** * spum_xts_tweak_in_payload() - Indicate that SPUM DOES place the XTS tweak * field in the packet payload (rather than using IV) * * Return: 1
*/
u8 spum_xts_tweak_in_payload(void)
{ return 1;
}
/** * spum_tx_status_len() - Return the length of the STATUS field in a SPU * response message. * * Return: Length of STATUS field in bytes.
*/
u8 spum_tx_status_len(void)
{ return SPU_TX_STATUS_LEN;
}
/** * spum_rx_status_len() - Return the length of the STATUS field in a SPU * response message. * * Return: Length of STATUS field in bytes.
*/
u8 spum_rx_status_len(void)
{ return SPU_RX_STATUS_LEN;
}
/** * spum_status_process() - Process the status from a SPU response message. * @statp: start of STATUS word * Return: * 0 - if status is good and response should be processed * !0 - status indicates an error and response is invalid
*/ int spum_status_process(u8 *statp)
{
u32 status;
status = __be32_to_cpu(*(__be32 *)statp);
flow_log("SPU response STATUS %#08x\n", status); if (status & SPU_STATUS_ERROR_FLAG) {
pr_err("%s() Warning: Error result from SPU: %#08x\n",
__func__, status); if (status & SPU_STATUS_INVALID_ICV) return SPU_INVALID_ICV; return -EBADMSG;
} return 0;
}
/** * spum_ccm_update_iv() - Update the IV as per the requirements for CCM mode. * * @digestsize: Digest size of this request * @cipher_parms: (pointer to) cipher parmaeters, includes IV buf & IV len * @assoclen: Length of AAD data * @chunksize: length of input data to be sent in this req * @is_encrypt: true if this is an output/encrypt operation * @is_esp: true if this is an ESP / RFC4309 operation *
*/ void spum_ccm_update_iv(unsignedint digestsize, struct spu_cipher_parms *cipher_parms, unsignedint assoclen, unsignedint chunksize, bool is_encrypt, bool is_esp)
{
u8 L; /* L from CCM algorithm, length of plaintext data */
u8 mprime; /* M' from CCM algo, (M - 2) / 2, where M=authsize */
u8 adata;
if (cipher_parms->iv_len != CCM_AES_IV_SIZE) {
pr_err("%s(): Invalid IV len %d for CCM mode, should be %d\n",
__func__, cipher_parms->iv_len, CCM_AES_IV_SIZE); return;
}
/* * IV needs to be formatted as follows: * * | Byte 0 | Bytes 1 - N | Bytes (N+1) - 15 | * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Bits 7 - 0 | Bits 7 - 0 | * | 0 |Ad?|(M - 2) / 2| L - 1 | Nonce | Plaintext Length | * * Ad? = 1 if AAD present, 0 if not present * M = size of auth field, 8, 12, or 16 bytes (SPU-M) -or- * 4, 6, 8, 10, 12, 14, 16 bytes (SPU2) * L = Size of Plaintext Length field; Nonce size = 15 - L * * It appears that the crypto API already expects the L-1 portion * to be set in the first byte of the IV, which implicitly determines * the nonce size, and also fills in the nonce. But the other bits * in byte 0 as well as the plaintext length need to be filled in. * * In rfc4309/esp mode, L is not already in the supplied IV and * we need to fill it in, as well as move the IV data to be after * the salt
*/ if (is_esp) {
L = CCM_ESP_L_VALUE; /* RFC4309 has fixed L */
} else { /* L' = plaintext length - 1 so Plaintext length is L' + 1 */
L = ((cipher_parms->iv_buf[0] & CCM_B0_L_PRIME) >>
CCM_B0_L_PRIME_SHIFT) + 1;
}
mprime = (digestsize - 2) >> 1; /* M' = (M - 2) / 2 */
adata = (assoclen > 0); /* adata = 1 if any associated data */
/* Nonce is already filled in by crypto API, and is 15 - L bytes */
/* Don't include digest in plaintext size when decrypting */ if (!is_encrypt)
chunksize -= digestsize;
/* Fill in length of plaintext, formatted to be L bytes long */
format_value_ccm(chunksize, &cipher_parms->iv_buf[15 - L + 1], L);
}
/** * spum_wordalign_padlen() - Given the length of a data field, determine the * padding required to align the data following this field on a 4-byte boundary. * @data_size: length of data field in bytes * * Return: length of status field padding, in bytes
*/
u32 spum_wordalign_padlen(u32 data_size)
{ return ((data_size + 3) & ~3) - data_size;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.