]> www.wagner.pp.ru Git - openssl-gost/engine.git/blob - gosthash2012.c
gosthash2012: Improve SIMD implementation
[openssl-gost/engine.git] / gosthash2012.c
1 /*
2  * GOST R 34.11-2012 core functions.
3  *
4  * Copyright (c) 2013 Cryptocom LTD.
5  * This file is distributed under the same license as OpenSSL.
6  *
7  * Author: Alexey Degtyarev <alexey@renatasystems.org>
8  *
9  */
10
11 #include "gosthash2012.h"
12 #ifdef __x86_64__
13 # include <immintrin.h>
14 # include <x86intrin.h>
15 #endif
16
17 #if defined(_WIN32) || defined(_WINDOWS)
18 # define INLINE __inline
19 #else
20 # define INLINE inline
21 #endif
22
23 #define BSWAP64(x) \
24     (((x & 0xFF00000000000000ULL) >> 56) | \
25      ((x & 0x00FF000000000000ULL) >> 40) | \
26      ((x & 0x0000FF0000000000ULL) >> 24) | \
27      ((x & 0x000000FF00000000ULL) >>  8) | \
28      ((x & 0x00000000FF000000ULL) <<  8) | \
29      ((x & 0x0000000000FF0000ULL) << 24) | \
30      ((x & 0x000000000000FF00ULL) << 40) | \
31      ((x & 0x00000000000000FFULL) << 56))
32
33 /*
34  * Initialize gost2012 hash context structure
35  */
36 void init_gost2012_hash_ctx(gost2012_hash_ctx * CTX,
37                             const unsigned int digest_size)
38 {
39     memset(CTX, 0, sizeof(gost2012_hash_ctx));
40
41     CTX->digest_size = digest_size;
42     /*
43      * IV for 512-bit hash should be 0^512
44      * IV for 256-bit hash should be (00000001)^64
45      *
46      * It's already zeroed when CTX is cleared above, so we only
47      * need to set it to 0x01-s for 256-bit hash.
48      */
49     if (digest_size == 256)
50         memset(&CTX->h, 0x01, sizeof(uint512_u));
51 }
52
53 static INLINE void pad(gost2012_hash_ctx * CTX)
54 {
55     memset(&(CTX->buffer.B[CTX->bufsize]), 0, sizeof(CTX->buffer) - CTX->bufsize);
56     CTX->buffer.B[CTX->bufsize] = 1;
57
58 }
59
60 static INLINE void add512(union uint512_u * RESTRICT x,
61                           const union uint512_u * RESTRICT y)
62 {
63 #ifndef __GOST3411_BIG_ENDIAN__
64     unsigned int CF = 0;
65     unsigned int i;
66
67 # ifdef __x86_64__
68     for (i = 0; i < 8; i++)
69         CF = _addcarry_u64(CF, x->QWORD[i] , y->QWORD[i], &(x->QWORD[i]));
70 # else
71     for (i = 0; i < 8; i++) {
72         const unsigned long long left = x->QWORD[i];
73         unsigned long long sum;
74
75         sum = left + y->QWORD[i] + CF;
76         /*
77          * (sum == left): is noop, because it's possible only
78          * when `left' is added with `0 + 0' or with `ULLONG_MAX + 1',
79          * in that case `CF' (carry) retain previous value, which is correct,
80          * because when `left + 0 + 0' there was no overflow (thus no carry),
81          * and when `left + ULLONG_MAX + 1' value is wrapped back to
82          * itself with overflow, thus creating carry.
83          *
84          * (sum != left):
85          * if `sum' is not wrapped (sum > left) there should not be carry,
86          * if `sum' is wrapped (sum < left) there should be carry.
87          */
88         if (sum != left)
89             CF = (sum < left);
90         x->QWORD[i] = sum;
91     }
92 # endif /* !__x86_64__ */
93 #else /* __GOST3411_BIG_ENDIAN__ */
94     const unsigned char *yp;
95     unsigned char *xp;
96     unsigned int i;
97     int buf;
98
99     xp = (unsigned char *)&x[0];
100     yp = (const unsigned char *)&y[0];
101
102     buf = 0;
103     for (i = 0; i < 64; i++) {
104         buf = xp[i] + yp[i] + (buf >> 8);
105         xp[i] = (unsigned char)buf & 0xFF;
106     }
107 #endif /* __GOST3411_BIG_ENDIAN__ */
108 }
109
110 static void g(union uint512_u *h, const union uint512_u * RESTRICT N,
111               const union uint512_u * RESTRICT m)
112 {
113 #ifdef __GOST3411_HAS_SSE2__
114     __m128i xmm0, xmm2, xmm4, xmm6; /* XMMR0-quadruple */
115     __m128i xmm1, xmm3, xmm5, xmm7; /* XMMR1-quadruple */
116     unsigned int i;
117
118     LOAD(N, xmm0, xmm2, xmm4, xmm6);
119     XLPS128M(h, xmm0, xmm2, xmm4, xmm6);
120
121     ULOAD(m, xmm1, xmm3, xmm5, xmm7);
122     XLPS128R(xmm0, xmm2, xmm4, xmm6, xmm1, xmm3, xmm5, xmm7);
123
124     for (i = 0; i < 11; i++)
125         ROUND128(i, xmm0, xmm2, xmm4, xmm6, xmm1, xmm3, xmm5, xmm7);
126
127     XLPS128M((&C[11]), xmm0, xmm2, xmm4, xmm6);
128     X128R(xmm0, xmm2, xmm4, xmm6, xmm1, xmm3, xmm5, xmm7);
129
130     X128M(h, xmm0, xmm2, xmm4, xmm6);
131     ULOAD(m, xmm1, xmm3, xmm5, xmm7);
132     X128R(xmm0, xmm2, xmm4, xmm6, xmm1, xmm3, xmm5, xmm7);
133
134     STORE(h, xmm0, xmm2, xmm4, xmm6);
135 #else
136     union uint512_u Ki, data;
137     unsigned int i;
138
139     XLPS(h, N, (&data));
140
141     /* Starting E() */
142     Ki = data;
143     XLPS((&Ki), ((const union uint512_u *)&m[0]), (&data));
144
145     for (i = 0; i < 11; i++)
146         ROUND(i, (&Ki), (&data));
147
148     XLPS((&Ki), (&C[11]), (&Ki));
149     X((&Ki), (&data), (&data));
150     /* E() done */
151
152     X((&data), h, (&data));
153     X((&data), m, h);
154 #endif
155 }
156
157 static INLINE void stage2(gost2012_hash_ctx * CTX, const union uint512_u *data)
158 {
159     g(&(CTX->h), &(CTX->N), data);
160
161     add512(&(CTX->N), &buffer512);
162     add512(&(CTX->Sigma), data);
163 }
164
165 static INLINE void stage3(gost2012_hash_ctx * CTX)
166 {
167     pad(CTX);
168     g(&(CTX->h), &(CTX->N), &(CTX->buffer));
169     add512(&(CTX->Sigma), &CTX->buffer);
170
171     memset(&(CTX->buffer.B[0]), 0, sizeof(uint512_u));
172 #ifndef __GOST3411_BIG_ENDIAN__
173     CTX->buffer.QWORD[0] = CTX->bufsize << 3;
174 #else
175     CTX->buffer.QWORD[0] = BSWAP64(CTX->bufsize << 3);
176 #endif
177     add512(&(CTX->N), &(CTX->buffer));
178
179     g(&(CTX->h), &buffer0, &(CTX->N));
180     g(&(CTX->h), &buffer0, &(CTX->Sigma));
181 }
182
183 /*
184  * Hash block of arbitrary length
185  *
186  */
187 void gost2012_hash_block(gost2012_hash_ctx * CTX,
188                          const unsigned char *data, size_t len)
189 {
190     register size_t chunksize;
191     register size_t bufsize = CTX->bufsize;
192
193     if (bufsize == 0) {
194         while (len >= 64) {
195             memcpy(&CTX->buffer.B[0], data, 64);
196             stage2(CTX, &(CTX->buffer));
197             data += 64;
198             len -= 64;
199         }
200     }
201
202     while (len) {
203         chunksize = 64 - bufsize;
204         if (chunksize > len)
205             chunksize = len;
206
207         memcpy(&CTX->buffer.B[bufsize], data, chunksize);
208
209         bufsize += chunksize;
210         len -= chunksize;
211         data += chunksize;
212
213         if (bufsize == 64) {
214             stage2(CTX, &(CTX->buffer) );
215             bufsize = 0;
216         }
217     }
218     CTX->bufsize = bufsize;
219 }
220
221 /*
222  * Compute hash value from current state of ctx
223  * state of hash ctx becomes invalid and cannot be used for further
224  * hashing.
225  */
226 void gost2012_finish_hash(gost2012_hash_ctx * CTX, unsigned char *digest)
227 {
228     stage3(CTX);
229
230     CTX->bufsize = 0;
231
232     if (CTX->digest_size == 256)
233         memcpy(digest, &(CTX->h.QWORD[4]), 32);
234     else
235         memcpy(digest, &(CTX->h.QWORD[0]), 64);
236 }