blob: 39e351e739d3d385853de60b83f178e5563f63eb [file] [log] [blame]
/*
*
* Embedded Linux library
*
* Copyright (C) 2011-2014 Intel Corporation. All rights reserved.
*
* This library 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 library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "util.h"
#include "checksum.h"
#include "private.h"
#ifndef HAVE_LINUX_IF_ALG_H
#ifndef HAVE_LINUX_TYPES_H
typedef uint8_t __u8;
typedef uint16_t __u16;
typedef uint32_t __u32;
#else
#include <linux/types.h>
#endif
#ifndef AF_ALG
#define AF_ALG 38
#define PF_ALG AF_ALG
#endif
struct sockaddr_alg {
__u16 salg_family;
__u8 salg_type[14];
__u32 salg_feat;
__u32 salg_mask;
__u8 salg_name[64];
};
/* Socket options */
#define ALG_SET_KEY 1
#else
#include <linux/if_alg.h>
#endif
#ifndef SOL_ALG
#define SOL_ALG 279
#endif
/**
* SECTION:checksum
* @short_description: Checksum handling
*
* Checksum handling
*/
#define is_valid_type(type) ((type) <= L_CHECKSUM_SHA512)
/**
* l_checksum:
*
* Opague object representing the checksum.
*/
struct l_checksum {
int sk;
char alg_name[16];
};
static int create_alg(const char *alg)
{
struct sockaddr_alg salg;
int sk;
sk = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
if (sk < 0)
return -1;
memset(&salg, 0, sizeof(salg));
salg.salg_family = AF_ALG;
strcpy((char *) salg.salg_type, "hash");
strcpy((char *) salg.salg_name, alg);
if (bind(sk, (struct sockaddr *) &salg, sizeof(salg)) < 0) {
close(sk);
return -1;
}
return sk;
}
/**
* l_checksum_new:
* @type: checksum type
*
* Creates new #l_checksum, using the checksum algorithm @type.
*
* Returns: a newly allocated #l_checksum object.
**/
LIB_EXPORT struct l_checksum *l_checksum_new(enum l_checksum_type type)
{
struct l_checksum *checksum;
const char *name;
int fd;
if (!is_valid_type(type))
return NULL;
switch (type) {
case L_CHECKSUM_NONE:
return NULL;
case L_CHECKSUM_MD4:
name = "md4";
break;
case L_CHECKSUM_MD5:
name = "md5";
break;
case L_CHECKSUM_SHA1:
name = "sha1";
break;
case L_CHECKSUM_SHA256:
name = "sha256";
break;
case L_CHECKSUM_SHA384:
name = "sha384";
break;
case L_CHECKSUM_SHA512:
name = "sha512";
break;
}
checksum = l_new(struct l_checksum, 1);
fd = create_alg(name);
if (fd < 0)
goto error;
checksum->sk = accept4(fd, NULL, 0, SOCK_CLOEXEC);
close(fd);
if (checksum->sk < 0)
goto error;
strcpy(checksum->alg_name, name);
return checksum;
error:
l_free(checksum);
return NULL;
}
LIB_EXPORT struct l_checksum *l_checksum_new_cmac_aes(const void *key,
size_t key_len)
{
struct l_checksum *checksum;
int fd;
fd = create_alg("cmac(aes)");
if (fd < 0)
return NULL;
if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {
close(fd);
return NULL;
}
checksum = l_new(struct l_checksum, 1);
checksum->sk = accept4(fd, NULL, 0, SOCK_CLOEXEC);
close(fd);
if (checksum->sk < 0) {
l_free(checksum);
return NULL;
}
strcpy(checksum->alg_name, "cmac(aes)");
return checksum;
}
struct l_checksum *l_checksum_new_hmac(enum l_checksum_type type,
const void *key, size_t key_len)
{
struct l_checksum *checksum;
int fd;
const char *name;
if (!is_valid_type(type))
return NULL;
switch (type) {
case L_CHECKSUM_NONE:
return NULL;
case L_CHECKSUM_MD4:
name = "hmac(md4)";
break;
case L_CHECKSUM_MD5:
name = "hmac(md5)";
break;
case L_CHECKSUM_SHA1:
name = "hmac(sha1)";
break;
case L_CHECKSUM_SHA256:
name = "hmac(sha256)";
break;
case L_CHECKSUM_SHA384:
name = "hmac(sha384)";
break;
case L_CHECKSUM_SHA512:
name = "hmac(sha512)";
break;
}
fd = create_alg(name);
if (fd < 0)
return NULL;
if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {
close(fd);
return NULL;
}
checksum = l_new(struct l_checksum, 1);
checksum->sk = accept4(fd, NULL, 0, SOCK_CLOEXEC);
close(fd);
if (checksum->sk < 0) {
l_free(checksum);
return NULL;
}
strcpy(checksum->alg_name, name);
return checksum;
}
/**
* l_checksum_clone:
* @checksum: parent checksum object
*
* Creates a new checksum with an independent copy of parent @checksum's
* state. l_checksum_get_digest can then be called on the parent or the
* clone without affecting the state of the other object.
**/
LIB_EXPORT struct l_checksum *l_checksum_clone(struct l_checksum *checksum)
{
struct l_checksum *clone;
if (unlikely(!checksum))
return NULL;
clone = l_new(struct l_checksum, 1);
clone->sk = accept4(checksum->sk, NULL, 0, SOCK_CLOEXEC);
if (clone->sk < 0) {
l_free(clone);
return NULL;
}
strcpy(clone->alg_name, checksum->alg_name);
return clone;
}
/**
* l_checksum_free:
* @checksum: checksum object
*
* Frees the memory allocated for @checksum.
**/
LIB_EXPORT void l_checksum_free(struct l_checksum *checksum)
{
if (unlikely(!checksum))
return;
close(checksum->sk);
l_free(checksum);
}
/**
* l_checksum_reset:
* @checksum: checksum object
*
* Resets the internal state of @checksum.
**/
void l_checksum_reset(struct l_checksum *checksum)
{
if (unlikely(!checksum))
return;
send(checksum->sk, NULL, 0, 0);
}
/**
* l_checksum_update:
* @checksum: checksum object
* @data: data pointer
* @len: length of data
*
* Updates checksum from @data pointer with @len bytes.
*
* Returns: true if the operation succeeded, false otherwise.
**/
LIB_EXPORT bool l_checksum_update(struct l_checksum *checksum,
const void *data, size_t len)
{
ssize_t written;
if (unlikely(!checksum))
return false;
written = send(checksum->sk, data, len, MSG_MORE);
if (written < 0)
return false;
return true;
}
/**
* l_checksum_updatev:
* @checksum: checksum object
* @iov: iovec pointer
* @iov_len: Number of iovec entries
*
* This is a iovec based version of l_checksum_update; it updates the checksum
* based on contents of @iov and @iov_len.
*
* Returns: true if the operation succeeded, false otherwise.
**/
bool l_checksum_updatev(struct l_checksum *checksum,
struct iovec *iov, size_t iov_len)
{
struct msghdr msg;
ssize_t written;
if (unlikely(!checksum))
return false;
if (unlikely(!iov) || unlikely(!iov_len))
return false;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = iov;
msg.msg_iovlen = iov_len;
written = sendmsg(checksum->sk, &msg, MSG_MORE);
if (written < 0)
return false;
return true;
}
/**
* l_checksum_get_digest:
* @checksum: checksum object
* @digest: digest data pointer
* @len: length of digest data
*
* Gets the digest from @checksum as raw binary data.
*
* Returns: Number of bytes read, or negative value if an error occurred.
**/
LIB_EXPORT ssize_t l_checksum_get_digest(struct l_checksum *checksum,
void *digest, size_t len)
{
ssize_t result;
if (unlikely(!checksum))
return -EINVAL;
if (unlikely(!digest))
return -EFAULT;
if (unlikely(!len))
return -EINVAL;
result = recv(checksum->sk, digest, len, 0);
if (result < 0)
return -errno;
return result;
}
/**
* l_checksum_get_string:
* @checksum: checksum object
*
* Gets the digest from @checksum as hex encoded string.
*
* Returns: a newly allocated hex string
**/
LIB_EXPORT char *l_checksum_get_string(struct l_checksum *checksum)
{
static struct {
const char *name;
size_t digest_len;
} digest_lut[] = {
{ .name = "md4", .digest_len = 16 },
{ .name = "md5", .digest_len = 16 },
{ .name = "sha1", .digest_len = 20 },
{ .name = "sha256", .digest_len = 32 },
{ .name = "sha384", .digest_len = 48 },
{ .name = "sha512", .digest_len = 64 },
{ .name = "cmac(aes)", .digest_len = 16 },
{ .name = "hmac(md4)", .digest_len = 16 },
{ .name = "hmac(md5)", .digest_len = 16 },
{ .name = "hmac(sha1)", .digest_len = 20 },
{ .name = "hmac(sha256)", .digest_len = 32 },
{ .name = "hmac(sha384)", .digest_len = 48 },
{ .name = "hmac(sha512)", .digest_len = 64 },
{ .name = NULL, .digest_len = 0 },
};
unsigned char digest[64];
unsigned int i;
if (unlikely(!checksum))
return NULL;
l_checksum_get_digest(checksum, digest, sizeof(digest));
for (i = 0; digest_lut[i].name; i++) {
if (strcmp(digest_lut[i].name, checksum->alg_name))
continue;
return l_util_hexstring(digest, digest_lut[i].digest_len);
}
return NULL;
}