summaryrefslogtreecommitdiffstats
path: root/tools/perf/util/blake2s.c
blob: ce5d89a1937635ada65fa42f3a69027591d9b919 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// SPDX-License-Identifier: GPL-2.0 OR MIT
/*
 * Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
 *
 * This is an implementation of the BLAKE2s hash and PRF functions.
 *
 * Information: https://blake2.net/
 */

#include "blake2s.h"
#include <linux/kernel.h>

static inline u32 ror32(u32 v, int n)
{
	return (v >> n) | (v << (32 - n));
}

static inline void le32_to_cpu_array(u32 a[], size_t n)
{
	for (size_t i = 0; i < n; i++)
		a[i] = le32_to_cpu((__force __le32)a[i]);
}

static inline void cpu_to_le32_array(u32 a[], size_t n)
{
	for (size_t i = 0; i < n; i++)
		a[i] = (__force u32)cpu_to_le32(a[i]);
}

static const u8 blake2s_sigma[10][16] = {
	{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
	{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
	{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
	{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
	{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
	{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
	{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
	{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
	{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
	{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
};

static inline void blake2s_increment_counter(struct blake2s_ctx *ctx, u32 inc)
{
	ctx->t[0] += inc;
	ctx->t[1] += (ctx->t[0] < inc);
}

static void blake2s_compress(struct blake2s_ctx *ctx,
			     const u8 *data, size_t nblocks, u32 inc)
{
	u32 m[16];
	u32 v[16];
	int i;

	while (nblocks > 0) {
		blake2s_increment_counter(ctx, inc);
		memcpy(m, data, BLAKE2S_BLOCK_SIZE);
		le32_to_cpu_array(m, ARRAY_SIZE(m));
		memcpy(v, ctx->h, 32);
		v[ 8] = BLAKE2S_IV0;
		v[ 9] = BLAKE2S_IV1;
		v[10] = BLAKE2S_IV2;
		v[11] = BLAKE2S_IV3;
		v[12] = BLAKE2S_IV4 ^ ctx->t[0];
		v[13] = BLAKE2S_IV5 ^ ctx->t[1];
		v[14] = BLAKE2S_IV6 ^ ctx->f[0];
		v[15] = BLAKE2S_IV7 ^ ctx->f[1];

#define G(r, i, a, b, c, d) do { \
	a += b + m[blake2s_sigma[r][2 * i + 0]]; \
	d = ror32(d ^ a, 16); \
	c += d; \
	b = ror32(b ^ c, 12); \
	a += b + m[blake2s_sigma[r][2 * i + 1]]; \
	d = ror32(d ^ a, 8); \
	c += d; \
	b = ror32(b ^ c, 7); \
} while (0)

#define ROUND(r) do { \
	G(r, 0, v[0], v[ 4], v[ 8], v[12]); \
	G(r, 1, v[1], v[ 5], v[ 9], v[13]); \
	G(r, 2, v[2], v[ 6], v[10], v[14]); \
	G(r, 3, v[3], v[ 7], v[11], v[15]); \
	G(r, 4, v[0], v[ 5], v[10], v[15]); \
	G(r, 5, v[1], v[ 6], v[11], v[12]); \
	G(r, 6, v[2], v[ 7], v[ 8], v[13]); \
	G(r, 7, v[3], v[ 4], v[ 9], v[14]); \
} while (0)
		ROUND(0);
		ROUND(1);
		ROUND(2);
		ROUND(3);
		ROUND(4);
		ROUND(5);
		ROUND(6);
		ROUND(7);
		ROUND(8);
		ROUND(9);

#undef G
#undef ROUND

		for (i = 0; i < 8; ++i)
			ctx->h[i] ^= v[i] ^ v[i + 8];

		data += BLAKE2S_BLOCK_SIZE;
		--nblocks;
	}
}

static inline void blake2s_set_lastblock(struct blake2s_ctx *ctx)
{
	ctx->f[0] = -1;
}

void blake2s_update(struct blake2s_ctx *ctx, const u8 *in, size_t inlen)
{
	const size_t fill = BLAKE2S_BLOCK_SIZE - ctx->buflen;

	if (unlikely(!inlen))
		return;
	if (inlen > fill) {
		memcpy(ctx->buf + ctx->buflen, in, fill);
		blake2s_compress(ctx, ctx->buf, 1, BLAKE2S_BLOCK_SIZE);
		ctx->buflen = 0;
		in += fill;
		inlen -= fill;
	}
	if (inlen > BLAKE2S_BLOCK_SIZE) {
		const size_t nblocks = DIV_ROUND_UP(inlen, BLAKE2S_BLOCK_SIZE);

		blake2s_compress(ctx, in, nblocks - 1, BLAKE2S_BLOCK_SIZE);
		in += BLAKE2S_BLOCK_SIZE * (nblocks - 1);
		inlen -= BLAKE2S_BLOCK_SIZE * (nblocks - 1);
	}
	memcpy(ctx->buf + ctx->buflen, in, inlen);
	ctx->buflen += inlen;
}

void blake2s_final(struct blake2s_ctx *ctx, u8 *out)
{
	blake2s_set_lastblock(ctx);
	memset(ctx->buf + ctx->buflen, 0,
	       BLAKE2S_BLOCK_SIZE - ctx->buflen); /* Padding */
	blake2s_compress(ctx, ctx->buf, 1, ctx->buflen);
	cpu_to_le32_array(ctx->h, ARRAY_SIZE(ctx->h));
	memcpy(out, ctx->h, ctx->outlen);
	memset(ctx, 0, sizeof(*ctx));
}