/*
 *  encdata.h
 *
 *  Copyright (c) 2022 Whamcloud
 */

#ifndef _ENCDATA_H
#define _ENCDATA_H

/* Define a fixed 4096-byte encryption unit size */
/* Must be identical to LUSTRE_ENCRYPTION_UNIT_SIZE */
#define EXT4_ENCRYPTION_BLOCKBITS 12
#define EXT4_ENCRYPTION_UNIT_SIZE ((size_t)1 << EXT4_ENCRYPTION_BLOCKBITS)
#define EXT4_ENCRYPTION_MASK      (~(EXT4_ENCRYPTION_UNIT_SIZE - 1))
#define LLCRYPT_SET_CONTEXT_MAX_SIZE	40
#define ENCDATA_XATTR_FMT_1 "{ encoding: "
#define ENCDATA_XATTR_FMT_2 ", size: "
#define ENCDATA_XATTR_FMT_3 ", enc_ctx: "
#define ENCDATA_XATTR_FMT_4 ", enc_name: "
#define ENCDATA_XATTR_FMT_END " }"
#define ENCDATA_XATTR_FMT_COMP  ENCDATA_XATTR_FMT_1 ENCDATA_XATTR_FMT_2 \
				ENCDATA_XATTR_FMT_3 ENCDATA_XATTR_FMT_4	\
				ENCDATA_XATTR_FMT_END

extern char encdata_xattr_fmt[NAME_MAX];

/*
 * base64url encoding, lifted from fs/crypto/fname.c.
 */

static const char base64url_table[] =
	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";

#define BASE64URL_CHARS(nbytes)	DIV_ROUND_UP((nbytes) * 4, 3)

/**
 * base64url_encode() - base64url-encode some binary data
 * @src: the binary data to encode
 * @srclen: the length of @src in bytes
 * @dst: (output) the base64url-encoded string.  Not NUL-terminated.
 *
 * Encodes data using base64url encoding, i.e. the "Base 64 Encoding with URL
 * and Filename Safe Alphabet" specified by RFC 4648.  '='-padding isn't used,
 * as it's unneeded and not required by the RFC.  base64url is used instead of
 * base64 to avoid the '/' character, which isn't allowed in filenames.
 *
 * Return: the length of the resulting base64url-encoded string in bytes.
 *	   This will be equal to LLCRYPT_BASE64URL_CHARS(srclen).
 */
static inline int base64url_encode(const u8 *src, int srclen, char *dst)
{
	u32 ac = 0;
	int bits = 0;
	int i;
	char *cp = dst;

	for (i = 0; i < srclen; i++) {
		ac = (ac << 8) | src[i];
		bits += 8;
		do {
			bits -= 6;
			*cp++ = base64url_table[(ac >> bits) & 0x3f];
		} while (bits >= 6);
	}
	if (bits)
		*cp++ = base64url_table[(ac << (6 - bits)) & 0x3f];
	return cp - dst;
}

/**
 * base64url_decode() - base64url-decode a string
 * @src: the string to decode.  Doesn't need to be NUL-terminated.
 * @srclen: the length of @src in bytes
 * @dst: (output) the decoded binary data
 *
 * Decodes a string using base64url encoding, i.e. the "Base 64 Encoding with
 * URL and Filename Safe Alphabet" specified by RFC 4648.  '='-padding isn't
 * accepted, nor are non-encoding characters such as whitespace.
 *
 * This implementation hasn't been optimized for performance.
 *
 * Return: the length of the resulting decoded binary data in bytes,
 *	   or -1 if the string isn't a valid base64url string.
 */
static inline int base64url_decode(const char *src, int srclen, u8 *dst)
{
	u32 ac = 0;
	int bits = 0;
	int i;
	u8 *bp = dst;

	for (i = 0; i < srclen; i++) {
		const char *p = strchr(base64url_table, src[i]);

		if (p == NULL || src[i] == 0)
			return -1;
		ac = (ac << 6) | (p - base64url_table);
		bits += 6;
		if (bits >= 8) {
			bits -= 8;
			*bp++ = (u8)(ac >> bits);
		}
	}
	if (ac & ((1 << bits) - 1))
		return -1;
	return bp - dst;
}

/* This version of the code uses base64url encoding for binary data. */
#define ENCDATA_ENCODING	"base64url"

/* Wrappers to support various encodings. Add new methods in there.
 */
static inline int encode(const u8 *src, int srclen, char *dst, char *encoding)
{
	if (!strcmp(encoding, "base64url"))
		return base64url_encode(src, srclen, dst);
	return -EINVAL;
}

static inline int decode(const char *src, int srclen, u8 *dst, char *encoding)
{
	if (!strcmp(encoding, "base64url"))
		return base64url_decode(src, srclen, dst);
	return -EINVAL;
}

#endif /* _ENCDATA_H */
