Add Poly1305 MAC
[libgcrypt.git] / cipher / mac-poly1305.c
1 /* mac-poly1305.c  -  Poly1305 based MACs
2  * Copyright (C) 2014 Jussi Kivilinna <jussi.kivilinna@iki.fi>
3  *
4  * This file is part of Libgcrypt.
5  *
6  * Libgcrypt is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser general Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * Libgcrypt is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25
26 #include "g10lib.h"
27 #include "mac-internal.h"
28 #include "poly1305-internal.h"
29
30
31 struct poly1305mac_context_s {
32   poly1305_context_t ctx;
33   struct {
34     unsigned int key_set:1;
35     unsigned int tag:1;
36   } marks;
37   byte tag[POLY1305_TAGLEN];
38   byte key[POLY1305_KEYLEN];
39 };
40
41
42 static gcry_err_code_t
43 poly1305mac_open (gcry_mac_hd_t h)
44 {
45   struct poly1305mac_context_s *mac_ctx;
46   int secure = (h->magic == CTX_MAGIC_SECURE);
47
48   if (secure)
49     mac_ctx = xtrycalloc_secure (1, sizeof(*mac_ctx));
50   else
51     mac_ctx = xtrycalloc (1, sizeof(*mac_ctx));
52
53   if (!mac_ctx)
54     return gpg_err_code_from_syserror ();
55
56   h->u.poly1305mac.ctx = mac_ctx;
57
58   return 0;
59 }
60
61
62 static void
63 poly1305mac_close (gcry_mac_hd_t h)
64 {
65   xfree(h->u.poly1305mac.ctx);
66 }
67
68
69 static gcry_err_code_t
70 poly1305mac_setkey (gcry_mac_hd_t h, const unsigned char *key, size_t keylen)
71 {
72   struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
73   gcry_err_code_t err;
74
75   memset(&mac_ctx->ctx, 0, sizeof(mac_ctx->ctx));
76   memset(&mac_ctx->tag, 0, sizeof(mac_ctx->tag));
77   memset(&mac_ctx->key, 0, sizeof(mac_ctx->key));
78
79   mac_ctx->marks.key_set = 0;
80   mac_ctx->marks.tag = 0;
81
82   if (keylen != POLY1305_KEYLEN)
83     return GPG_ERR_INV_KEYLEN;
84
85   memcpy(mac_ctx->key, key, POLY1305_KEYLEN);
86
87   err = _gcry_poly1305_init (&mac_ctx->ctx, mac_ctx->key, POLY1305_KEYLEN);
88   if (err)
89     {
90       memset(&mac_ctx->key, 0, sizeof(mac_ctx->key));
91       return err;
92     }
93
94   mac_ctx->marks.key_set = 1;
95
96   return 0;
97 }
98
99
100 static gcry_err_code_t
101 poly1305mac_reset (gcry_mac_hd_t h)
102 {
103   struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
104
105   if (!mac_ctx->marks.key_set)
106     return GPG_ERR_INV_STATE;
107
108   memset(&mac_ctx->ctx, 0, sizeof(mac_ctx->ctx));
109   memset(&mac_ctx->tag, 0, sizeof(mac_ctx->tag));
110
111   mac_ctx->marks.key_set = 1;
112   mac_ctx->marks.tag = 0;
113
114   return _gcry_poly1305_init (&mac_ctx->ctx, mac_ctx->key, POLY1305_KEYLEN);
115 }
116
117
118 static gcry_err_code_t
119 poly1305mac_write (gcry_mac_hd_t h, const unsigned char *buf, size_t buflen)
120 {
121   struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
122
123   if (!mac_ctx->marks.key_set || mac_ctx->marks.tag)
124     return GPG_ERR_INV_STATE;
125
126   _gcry_poly1305_update (&mac_ctx->ctx, buf, buflen);
127   return 0;
128 }
129
130
131 static gcry_err_code_t
132 poly1305mac_read (gcry_mac_hd_t h, unsigned char *outbuf, size_t *outlen)
133 {
134   struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
135
136   if (!mac_ctx->marks.key_set)
137     return GPG_ERR_INV_STATE;
138
139   if (!mac_ctx->marks.tag)
140     {
141       _gcry_poly1305_finish(&mac_ctx->ctx, mac_ctx->tag);
142
143       memset(&mac_ctx->ctx, 0, sizeof(mac_ctx->ctx));
144       mac_ctx->marks.tag = 1;
145     }
146
147   if (*outlen <= POLY1305_TAGLEN)
148     buf_cpy (outbuf, mac_ctx->tag, *outlen);
149   else
150     {
151       buf_cpy (outbuf, mac_ctx->tag, POLY1305_TAGLEN);
152       *outlen = POLY1305_TAGLEN;
153     }
154
155   return 0;
156 }
157
158
159 static gcry_err_code_t
160 poly1305mac_verify (gcry_mac_hd_t h, const unsigned char *buf, size_t buflen)
161 {
162   struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
163   gcry_err_code_t err;
164   size_t outlen = 0;
165
166   /* Check and finalize tag. */
167   err = poly1305mac_read(h, NULL, &outlen);
168   if (err)
169     return err;
170
171   if (buflen > POLY1305_TAGLEN)
172     return GPG_ERR_INV_LENGTH;
173
174   return buf_eq_const (buf, mac_ctx->tag, buflen) ? 0 : GPG_ERR_CHECKSUM;
175 }
176
177
178 static unsigned int
179 poly1305mac_get_maclen (int algo)
180 {
181   (void)algo;
182
183   return POLY1305_TAGLEN;
184 }
185
186
187 static unsigned int
188 poly1305mac_get_keylen (int algo)
189 {
190   (void)algo;
191
192   return POLY1305_KEYLEN;
193 }
194
195
196 static gcry_mac_spec_ops_t poly1305mac_ops = {
197   poly1305mac_open,
198   poly1305mac_close,
199   poly1305mac_setkey,
200   NULL,
201   poly1305mac_reset,
202   poly1305mac_write,
203   poly1305mac_read,
204   poly1305mac_verify,
205   poly1305mac_get_maclen,
206   poly1305mac_get_keylen
207 };
208
209
210 gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac = {
211   GCRY_MAC_POLY1305, {0, 0}, "POLY1305",
212   &poly1305mac_ops
213 };