890e94a8adee28eda1ff111fb8e31d2b6c42dab8
[platform/upstream/nettle.git] / umac-poly128.c
1 /* umac-poly128.c
2
3    Copyright (C) 2013 Niels Möller
4
5    This file is part of GNU Nettle.
6
7    GNU Nettle is free software: you can redistribute it and/or
8    modify it under the terms of either:
9
10      * the GNU Lesser General Public License as published by the Free
11        Software Foundation; either version 3 of the License, or (at your
12        option) any later version.
13
14    or
15
16      * the GNU General Public License as published by the Free
17        Software Foundation; either version 2 of the License, or (at your
18        option) any later version.
19
20    or both in parallel, as here.
21
22    GNU Nettle is distributed in the hope that it will be useful,
23    but WITHOUT ANY WARRANTY; without even the implied warranty of
24    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25    General Public License for more details.
26
27    You should have received copies of the GNU General Public License and
28    the GNU Lesser General Public License along with this program.  If
29    not, see http://www.gnu.org/licenses/.
30 */
31
32 #if HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35
36 #include <assert.h>
37
38 #include "umac.h"
39
40 #define HI(x) (x >> 32)
41 #define LO(x) (x & 0xffffffffUL)
42
43 static void
44 poly128_mul (const uint32_t *k, uint64_t *y)
45 {
46   uint64_t y0,y1,y2,y3,p0,p1,p2,p3,m0,m1,m2;
47   y0 = LO (y[1]);
48   y1 = HI (y[1]);
49   y2 = LO (y[0]);
50   y3 = HI (y[0]);
51
52   p0 = y0 * k[3];
53   m0 = y0 * k[2] + y1 * k[3];
54   p1 = y0 * k[1] + y1 * k[2] + y2 * k[3];
55   m1 = y0 * k[0] + y1 * k[1] + y2 * k[2] + y3 * k[3];
56   p2 = y1 * k[0] + y2 * k[1] + y3 * k[2];
57   m2 = y2 * k[0] + y3 * k[1];
58   p3 = y3 * k[0];
59
60   /* Collaps to 4 64-bit words,
61      +---+---+---+---+
62      | p3| p2| p1| p0|
63      +-+-+-+-+-+-+-+-+
64     +  | m2| m1| m0|
65     -+-+-+-+-+-+-+-+-+
66   */
67   /* But it's convenient to reduce (p3,p2,p1,p0) and (m2,m1,m0) mod p first.*/
68   m1 += UMAC_P128_OFFSET * HI(p3);
69   p1 += UMAC_P128_OFFSET * (LO(p3) + HI(m2));
70   m0 += UMAC_P128_OFFSET * (HI(p2) + LO(m2));
71   p0 += UMAC_P128_OFFSET * (LO(p2) + HI(m1));
72
73   /* Left to add
74      +---+---+
75      | p1| p0|
76      +-+-+-+-+
77      m1| m0|
78      +-+---+
79   */
80   /* First add high parts, with no possibilities for carries */
81   p1 += m0 >> 32;
82
83   m0 <<= 32;
84   m1 <<= 32;
85
86   /* Remains:
87      +---+---+
88      | p1| p0|
89      +-+-+---+
90     +| m1| m0|
91     -+---+---+
92   */
93   p0 += m0;
94   p1 += (p0 < m0);
95   p1 += m1;
96   if (p1 < m1)
97     {
98       p0 += UMAC_P128_OFFSET;
99       p1 += (p0 < UMAC_P128_OFFSET);
100     }
101
102   y[0] = p1;
103   y[1] = p0;
104 }
105
106 void
107 _umac_poly128 (const uint32_t *k, uint64_t *y, uint64_t mh, uint64_t ml)
108 {
109   uint64_t yh, yl, cy;
110
111   if ( (mh >> 32) == 0xffffffff)
112     {
113       poly128_mul (k, y);
114       if (y[1] > 0)
115         y[1]--;
116       else if (y[0] > 0)
117         {
118           y[0]--;
119           y[1] = UMAC_P128_HI;
120         }
121       else
122         {
123           y[0] = UMAC_P128_HI;
124           y[1] = UMAC_P128_LO-1;
125         }
126
127       mh -= (ml < UMAC_P128_OFFSET);
128       ml -= UMAC_P128_OFFSET;
129     }
130   assert (mh < UMAC_P128_HI || ml < UMAC_P128_LO);
131
132   poly128_mul (k, y);
133   yl = y[1] + ml;
134   cy = (yl < ml);
135   yh = y[0] + cy;
136   cy = (yh < cy);
137   yh += mh;
138   cy += (yh < mh);
139   assert (cy <= 1);
140   if (cy)
141     {
142       yl += UMAC_P128_OFFSET;
143       yh += yl < UMAC_P128_OFFSET;
144     }
145
146   y[0] = yh;
147   y[1] = yl;
148 }