X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=lib%2Fcrypto_backend%2Fcrypto_cipher_kernel.c;h=3460717920790881fc02c85540bc6b4599704848;hb=420ad62f0bb77970a6ba234bde9e1405f7df7789;hp=515039ee06457f51ead25c21da84af443af84948;hpb=a4585423fd38508084178391b0f420dd57f7260a;p=platform%2Fupstream%2Fcryptsetup.git diff --git a/lib/crypto_backend/crypto_cipher_kernel.c b/lib/crypto_backend/crypto_cipher_kernel.c index 515039e..3460717 100644 --- a/lib/crypto_backend/crypto_cipher_kernel.c +++ b/lib/crypto_backend/crypto_cipher_kernel.c @@ -1,32 +1,37 @@ /* * Linux kernel userspace API crypto backend implementation (skcipher) * - * Copyright (C) 2012, Red Hat, Inc. All rights reserved. - * Copyright (C) 2012, Milan Broz + * Copyright (C) 2012-2023 Red Hat, Inc. All rights reserved. + * Copyright (C) 2012-2023 Milan Broz * - * 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. + * This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. * - * This program is distributed in the hope that it will be useful, + * This file 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. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 + * You should have received a copy of the GNU Lesser General Public + * License along with this file; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include +#include #include #include #include #include +#include "crypto_backend_internal.h" + +#ifdef ENABLE_AF_ALG + #include -#include "crypto_backend.h" #ifndef AF_ALG #define AF_ALG 38 @@ -35,77 +40,80 @@ #define SOL_ALG 279 #endif -struct crypt_cipher { - int tfmfd; - int opfd; -}; +#ifndef ALG_SET_AEAD_AUTHSIZE +#define ALG_SET_AEAD_AUTHSIZE 5 +#endif -int crypt_kernel_socket_init(struct sockaddr_alg *sa, int *tfmfd, int *opfd) +/* + * ciphers + * + * ENOENT - algorithm not available + * ENOTSUP - AF_ALG family not available + * (but cannot check specifically for skcipher API) + */ +static int _crypt_cipher_init(struct crypt_cipher_kernel *ctx, + const void *key, size_t key_length, + size_t tag_length, struct sockaddr_alg *sa) { - *tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0); - if (*tfmfd == -1) + if (!ctx) + return -EINVAL; + + ctx->opfd = -1; + ctx->tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0); + if (ctx->tfmfd < 0) { + crypt_cipher_destroy_kernel(ctx); return -ENOTSUP; + } - if (bind(*tfmfd, (struct sockaddr *)sa, sizeof(*sa)) == -1) { - close(*tfmfd); - *tfmfd = -1; + if (bind(ctx->tfmfd, (struct sockaddr *)sa, sizeof(*sa)) < 0) { + crypt_cipher_destroy_kernel(ctx); return -ENOENT; } - *opfd = accept(*tfmfd, NULL, 0); - if (*opfd == -1) { - close(*tfmfd); - *tfmfd = -1; + if (setsockopt(ctx->tfmfd, SOL_ALG, ALG_SET_KEY, key, key_length) < 0) { + crypt_cipher_destroy_kernel(ctx); + return -EINVAL; + } + + if (tag_length && setsockopt(ctx->tfmfd, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, NULL, tag_length) < 0) { + crypt_cipher_destroy_kernel(ctx); + return -EINVAL; + } + + ctx->opfd = accept(ctx->tfmfd, NULL, 0); + if (ctx->opfd < 0) { + crypt_cipher_destroy_kernel(ctx); return -EINVAL; } return 0; } -/* - *ciphers - * - * ENOENT - algorithm not available - * ENOTSUP - AF_ALG family not available - * (but cannot check specificaly for skcipher API) - */ -int crypt_cipher_init(struct crypt_cipher **ctx, const char *name, - const char *mode, const void *buffer, size_t length) +int crypt_cipher_init_kernel(struct crypt_cipher_kernel *ctx, const char *name, + const char *mode, const void *key, size_t key_length) { - struct crypt_cipher *h; struct sockaddr_alg sa = { .salg_family = AF_ALG, .salg_type = "skcipher", }; int r; - h = malloc(sizeof(*h)); - if (!h) - return -ENOMEM; - - snprintf((char *)sa.salg_name, sizeof(sa.salg_name), - "%s(%s)", mode, name); + if (!strcmp(name, "cipher_null")) + key_length = 0; - r = crypt_kernel_socket_init(&sa, &h->tfmfd, &h->opfd); - if (r < 0) { - free(h); - return r; - } - - if (setsockopt(h->tfmfd, SOL_ALG, ALG_SET_KEY, buffer, length) == -1) { - crypt_cipher_destroy(h); + r = snprintf((char *)sa.salg_name, sizeof(sa.salg_name), "%s(%s)", mode, name); + if (r < 0 || (size_t)r >= sizeof(sa.salg_name)) return -EINVAL; - } - *ctx = h; - return 0; + return _crypt_cipher_init(ctx, key, key_length, 0, &sa); } /* The in/out should be aligned to page boundary */ -static int crypt_cipher_crypt(struct crypt_cipher *ctx, - const char *in, char *out, size_t length, - const char *iv, size_t iv_length, - uint32_t direction) +static int _crypt_cipher_crypt(struct crypt_cipher_kernel *ctx, + const char *in, size_t in_length, + char *out, size_t out_length, + const char *iv, size_t iv_length, + uint32_t direction) { int r = 0; ssize_t len; @@ -114,7 +122,7 @@ static int crypt_cipher_crypt(struct crypt_cipher *ctx, uint32_t *type; struct iovec iov = { .iov_base = (void*)(uintptr_t)in, - .iov_len = length, + .iov_len = in_length, }; int iv_msg_size = iv ? CMSG_SPACE(sizeof(*alg_iv) + iv_length) : 0; char buffer[CMSG_SPACE(sizeof(*type)) + iv_msg_size]; @@ -125,7 +133,7 @@ static int crypt_cipher_crypt(struct crypt_cipher *ctx, .msg_iovlen = 1, }; - if (!in || !out || !length) + if (!in || !out || !in_length) return -EINVAL; if ((!iv && iv_length) || (iv && !iv_length)) @@ -135,6 +143,9 @@ static int crypt_cipher_crypt(struct crypt_cipher *ctx, /* Set encrypt/decrypt operation */ header = CMSG_FIRSTHDR(&msg); + if (!header) + return -EINVAL; + header->cmsg_level = SOL_ALG; header->cmsg_type = ALG_SET_OP; header->cmsg_len = CMSG_LEN(sizeof(*type)); @@ -144,6 +155,9 @@ static int crypt_cipher_crypt(struct crypt_cipher *ctx, /* Set IV */ if (iv) { header = CMSG_NXTHDR(&msg, header); + if (!header) + return -EINVAL; + header->cmsg_level = SOL_ALG; header->cmsg_type = ALG_SET_IV; header->cmsg_len = iv_msg_size; @@ -153,42 +167,185 @@ static int crypt_cipher_crypt(struct crypt_cipher *ctx, } len = sendmsg(ctx->opfd, &msg, 0); - if (len != (ssize_t)length) { + if (len != (ssize_t)(in_length)) r = -EIO; - goto bad; + else { + len = read(ctx->opfd, out, out_length); + if (len != (ssize_t)out_length) + r = -EIO; } - len = read(ctx->opfd, out, length); - if (len != (ssize_t)length) - r = -EIO; -bad: - memset(buffer, 0, sizeof(buffer)); + crypt_backend_memzero(buffer, sizeof(buffer)); return r; } -int crypt_cipher_encrypt(struct crypt_cipher *ctx, - const char *in, char *out, size_t length, - const char *iv, size_t iv_length) +int crypt_cipher_encrypt_kernel(struct crypt_cipher_kernel *ctx, + const char *in, char *out, size_t length, + const char *iv, size_t iv_length) { - return crypt_cipher_crypt(ctx, in, out, length, - iv, iv_length, ALG_OP_ENCRYPT); + return _crypt_cipher_crypt(ctx, in, length, out, length, + iv, iv_length, ALG_OP_ENCRYPT); } -int crypt_cipher_decrypt(struct crypt_cipher *ctx, - const char *in, char *out, size_t length, - const char *iv, size_t iv_length) +int crypt_cipher_decrypt_kernel(struct crypt_cipher_kernel *ctx, + const char *in, char *out, size_t length, + const char *iv, size_t iv_length) { - return crypt_cipher_crypt(ctx, in, out, length, - iv, iv_length, ALG_OP_DECRYPT); + return _crypt_cipher_crypt(ctx, in, length, out, length, + iv, iv_length, ALG_OP_DECRYPT); } -int crypt_cipher_destroy(struct crypt_cipher *ctx) +void crypt_cipher_destroy_kernel(struct crypt_cipher_kernel *ctx) { - if (ctx->tfmfd != -1) + if (ctx->tfmfd >= 0) close(ctx->tfmfd); - if (ctx->opfd != -1) + if (ctx->opfd >= 0) close(ctx->opfd); - memset(ctx, 0, sizeof(*ctx)); - free(ctx); + + ctx->tfmfd = -1; + ctx->opfd = -1; +} + +int crypt_cipher_check_kernel(const char *name, const char *mode, + const char *integrity, size_t key_length) +{ + struct crypt_cipher_kernel c; + char mode_name[64], tmp_salg_name[180], *real_mode = NULL, *cipher_iv = NULL, *key; + const char *salg_type; + bool aead; + int r; + struct sockaddr_alg sa = { + .salg_family = AF_ALG, + }; + + aead = integrity && strcmp(integrity, "none"); + + /* Remove IV if present */ + if (mode) { + strncpy(mode_name, mode, sizeof(mode_name)); + mode_name[sizeof(mode_name) - 1] = 0; + cipher_iv = strchr(mode_name, '-'); + if (cipher_iv) { + *cipher_iv = '\0'; + real_mode = mode_name; + } + } + + salg_type = aead ? "aead" : "skcipher"; + r = snprintf((char *)sa.salg_type, sizeof(sa.salg_type), "%s", salg_type); + if (r < 0 || (size_t)r >= sizeof(sa.salg_name)) + return -EINVAL; + + memset(tmp_salg_name, 0, sizeof(tmp_salg_name)); + + /* FIXME: this is duplicating a part of devmapper backend */ + if (aead && !strcmp(integrity, "poly1305")) + r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "rfc7539(%s,%s)", name, integrity); + else if (!real_mode) + r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "%s", name); + else if (aead && !strcmp(real_mode, "ccm")) + r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "rfc4309(%s(%s))", real_mode, name); + else + r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "%s(%s)", real_mode, name); + + if (r < 0 || (size_t)r >= sizeof(tmp_salg_name)) + return -EINVAL; + + memcpy(sa.salg_name, tmp_salg_name, sizeof(sa.salg_name)); + + key = malloc(key_length); + if (!key) + return -ENOMEM; + + /* We cannot use RNG yet, any key works here, tweak the first part if it is split key (XTS). */ + memset(key, 0xab, key_length); + *key = 0xef; + + r = _crypt_cipher_init(&c, key, key_length, 0, &sa); + crypt_cipher_destroy_kernel(&c); + free(key); + + return r; +} + +int crypt_bitlk_decrypt_key_kernel(const void *key, size_t key_length, + const char *in, char *out, size_t length, + const char *iv, size_t iv_length, + const char *tag, size_t tag_length) +{ + struct crypt_cipher_kernel c; + struct sockaddr_alg sa = { + .salg_family = AF_ALG, + .salg_type = "aead", + .salg_name = "ccm(aes)", + }; + int r; + char buffer[128], ccm_iv[16]; + + if (length + tag_length > sizeof(buffer)) + return -EINVAL; + + if (iv_length > sizeof(ccm_iv) - 2) + return -EINVAL; + + r = _crypt_cipher_init(&c, key, key_length, tag_length, &sa); + if (r < 0) + return r; + + memcpy(buffer, in, length); + memcpy(buffer + length, tag, tag_length); + + /* CCM IV - RFC3610 */ + memset(ccm_iv, 0, sizeof(ccm_iv)); + ccm_iv[0] = 15 - iv_length - 1; + memcpy(ccm_iv + 1, iv, iv_length); + memset(ccm_iv + 1 + iv_length, 0, ccm_iv[0] + 1); + iv_length = sizeof(ccm_iv); + + r = _crypt_cipher_crypt(&c, buffer, length + tag_length, out, length, + ccm_iv, iv_length, ALG_OP_DECRYPT); + + crypt_cipher_destroy_kernel(&c); + crypt_backend_memzero(buffer, sizeof(buffer)); + + return r; +} + +#else /* ENABLE_AF_ALG */ +int crypt_cipher_init_kernel(struct crypt_cipher_kernel *ctx, const char *name, + const char *mode, const void *key, size_t key_length) +{ + return -ENOTSUP; +} + +void crypt_cipher_destroy_kernel(struct crypt_cipher_kernel *ctx) +{ + return; +} + +int crypt_cipher_encrypt_kernel(struct crypt_cipher_kernel *ctx, + const char *in, char *out, size_t length, + const char *iv, size_t iv_length) +{ + return -EINVAL; +} +int crypt_cipher_decrypt_kernel(struct crypt_cipher_kernel *ctx, + const char *in, char *out, size_t length, + const char *iv, size_t iv_length) +{ + return -EINVAL; +} +int crypt_cipher_check_kernel(const char *name, const char *mode, + const char *integrity, size_t key_length) +{ + /* Cannot check, expect success. */ return 0; } +int crypt_bitlk_decrypt_key_kernel(const void *key, size_t key_length, + const char *in, char *out, size_t length, + const char *iv, size_t iv_length, + const char *tag, size_t tag_length) +{ + return -ENOTSUP; +} +#endif