upstream: https://github.com/mirage/mirage-crypto
1/* Copyright (c) 2017 David Kaloper Meršinjak. All rights reserved.
2 See LICENSE.md. */
3
4#include "crypto.h"
5#include <string.h>
6
7/* Generic table-driven GHASH.
8 *
9 * References:
10 * - The Galois/Counter Mode of Operation. David A. McGrew and John Viega.
11 * - NIST SP 800-38D. Recommendation for Block Cipher Modes of Operation:
12 * Galois/Counter Mode (GCM) and GMAC.
13 */
14
15/* LARGE_TABLES -> 65K per key
16 * !LARGE_TABLES -> 8K per key, ~3x slower. */
17#define __MC_GHASH_LARGE_TABLES
18
19/* 64-bit Windows sets ARCH_64BIT but 128-bit integers are not supported
20 * by the Microsoft compiler. Drop down to 32-bit for MSVC;
21 * ghash_ctmul.c will implement ghash for MSVC.
22 */
23#if defined(ARCH_64BIT) && !defined(_MSC_VER)
24
25#define __set_uint128_t(w1, w0) (((__uint128_t) w1 << 64) | w0)
26
27static const __uint128_t r = __set_uint128_t (0xe100000000000000, 0);
28
29static inline __uint128_t __load_128_t (const uint64_t s[2]) {
30 return __set_uint128_t (be64_to_cpu (s[0]), be64_to_cpu (s[1]));
31}
32
33static inline __uint128_t __load_128_t_with_padding (const uint8_t *src, size_t n) {
34 uint64_t buf[2] = { 0 };
35 memcpy (buf, src, n);
36 return __load_128_t (buf);
37}
38
39static inline void __store_128_t (uint64_t s[2], __uint128_t x) {
40 s[0] = cpu_to_be64 (x >> 64);
41 s[1] = cpu_to_be64 (x);
42}
43
44#if defined (__MC_GHASH_LARGE_TABLES)
45#define __t_width 8 // coefficient window
46#define __t_tables 16 // 128 / t_width
47#define __t_size 4096 // 2^t_width * t_tables
48#else
49#define __t_width 4
50#define __t_tables 32
51#define __t_size 512
52#endif
53
54static inline __uint128_t __gfmul (__uint128_t a, __uint128_t b) {
55 __uint128_t z = 0,
56 v = a;
57 for (int i = 0; i < 128; i ++) {
58 if ((uint64_t) (b >> (127 - i)) & 1)
59 z = z ^ v;
60 v = (uint64_t) v & 1 ? (v >> 1) ^ r : v >> 1;
61 }
62 return z;
63}
64
65// NB Exponents are reversed.
66// TODO: Fast table derivation.
67static inline void __derive (uint64_t key[2], __uint128_t m[__t_size]) {
68 __uint128_t e = 1 << (__t_width - 1),
69 h = __load_128_t (key);
70 for (int i = 0; i < __t_tables; i ++, e <<= __t_width) {
71 __uint128_t exph = __gfmul (h, e);
72 for (int j = 0; j < (1 << __t_width); j ++)
73 m[(i << __t_width) | j] = __gfmul (exph, (__uint128_t) j << (128 - __t_width));
74 }
75}
76
77#define __t_mask ((1 << __t_width) - 1)
78static inline __uint128_t __gfmul_tab (__uint128_t m[__t_size], __uint128_t x) {
79 __uint128_t r = 0;
80 for (int i = 0; i < __t_tables; i ++)
81 r ^= m[(i << __t_width) | ((uint8_t) (x >> (i * __t_width)) & __t_mask)];
82 return r;
83}
84
85static inline void __ghash (__uint128_t m[__t_size], uint64_t hash[2], const uint8_t *src, size_t n) {
86 __uint128_t acc = __load_128_t (hash);
87 for (; n >= 16; src += 16, n -= 16)
88 acc = __gfmul_tab (m, acc ^ __load_128_t ((uint64_t *) src));
89 if (n > 0)
90 acc = __gfmul_tab (m, acc ^ __load_128_t_with_padding (src, n));
91 __store_128_t (hash, acc);
92}
93
94CAMLprim value mc_ghash_key_size_generic (__unit ()) {
95 return Val_int (sizeof (__uint128_t) * __t_size);
96}
97
98CAMLprim value mc_ghash_init_key_generic (value key, value m) {
99 __derive ((uint64_t *) _st_uint8 (key), (__uint128_t *) Bp_val (m));
100 return Val_unit;
101}
102
103CAMLprim value
104mc_ghash_generic (value m, value hash, value src, value off, value len) {
105 __ghash ((__uint128_t *) Bp_val (m), (uint64_t *) Bp_val (hash),
106 _st_uint8_off (src, off), Int_val (len) );
107 return Val_unit;
108}
109
110#endif /* ARCH_64BIT */