Line data Source code
1 : /* cipher-xts.c - XTS mode implementation
2 : * Copyright (C) 2017 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 "cipher.h"
28 : #include "bufhelp.h"
29 : #include "./cipher-internal.h"
30 :
31 :
32 2579600 : static inline void xts_gfmul_byA (unsigned char *out, const unsigned char *in)
33 : {
34 2579600 : u64 hi = buf_get_le64 (in + 8);
35 2579600 : u64 lo = buf_get_le64 (in + 0);
36 2579600 : u64 carry = -(hi >> 63) & 0x87;
37 :
38 2579600 : hi = (hi << 1) + (lo >> 63);
39 2579600 : lo = (lo << 1) ^ carry;
40 :
41 2579600 : buf_put_le64 (out + 8, hi);
42 2579600 : buf_put_le64 (out + 0, lo);
43 2579600 : }
44 :
45 :
46 53518 : static inline void xts_inc128 (unsigned char *seqno)
47 : {
48 53518 : u64 lo = buf_get_le64 (seqno + 0);
49 53518 : u64 hi = buf_get_le64 (seqno + 8);
50 :
51 53518 : hi += !(++lo);
52 :
53 53518 : buf_put_le64 (seqno + 0, lo);
54 53518 : buf_put_le64 (seqno + 8, hi);
55 53518 : }
56 :
57 :
58 : gcry_err_code_t
59 53518 : _gcry_cipher_xts_crypt (gcry_cipher_hd_t c,
60 : unsigned char *outbuf, size_t outbuflen,
61 : const unsigned char *inbuf, size_t inbuflen,
62 : int encrypt)
63 : {
64 53518 : gcry_cipher_encrypt_t tweak_fn = c->spec->encrypt;
65 53518 : gcry_cipher_encrypt_t crypt_fn =
66 53518 : encrypt ? c->spec->encrypt : c->spec->decrypt;
67 : union
68 : {
69 : cipher_context_alignment_t xcx;
70 : byte x1[GCRY_XTS_BLOCK_LEN];
71 : u64 x64[GCRY_XTS_BLOCK_LEN / sizeof(u64)];
72 : } tmp;
73 : unsigned int burn, nburn;
74 : size_t nblocks;
75 :
76 53518 : if (c->spec->blocksize != GCRY_XTS_BLOCK_LEN)
77 0 : return GPG_ERR_CIPHER_ALGO;
78 53518 : if (outbuflen < inbuflen)
79 0 : return GPG_ERR_BUFFER_TOO_SHORT;
80 53518 : if (inbuflen < GCRY_XTS_BLOCK_LEN)
81 0 : return GPG_ERR_BUFFER_TOO_SHORT;
82 :
83 : /* Data-unit max length: 2^20 blocks. */
84 53518 : if (inbuflen > GCRY_XTS_BLOCK_LEN << 20)
85 0 : return GPG_ERR_INV_LENGTH;
86 :
87 53518 : nblocks = inbuflen / GCRY_XTS_BLOCK_LEN;
88 53518 : nblocks -= !encrypt && (inbuflen % GCRY_XTS_BLOCK_LEN) != 0;
89 :
90 : /* Generate first tweak value. */
91 53518 : burn = tweak_fn (c->u_mode.xts.tweak_context, c->u_ctr.ctr, c->u_iv.iv);
92 :
93 : /* Use a bulk method if available. */
94 53518 : if (nblocks && c->bulk.xts_crypt)
95 : {
96 0 : c->bulk.xts_crypt (c, c->u_ctr.ctr, outbuf, inbuf, nblocks, encrypt);
97 0 : inbuf += nblocks * GCRY_XTS_BLOCK_LEN;
98 0 : outbuf += nblocks * GCRY_XTS_BLOCK_LEN;
99 0 : inbuflen -= nblocks * GCRY_XTS_BLOCK_LEN;
100 0 : nblocks = 0;
101 : }
102 :
103 : /* If we don't have a bulk method use the standard method. We also
104 : use this method for the a remaining partial block. */
105 :
106 2686608 : while (nblocks)
107 : {
108 : /* Xor-Encrypt/Decrypt-Xor block. */
109 2579572 : buf_xor (tmp.x64, inbuf, c->u_ctr.ctr, GCRY_XTS_BLOCK_LEN);
110 2579572 : nburn = crypt_fn (&c->context.c, tmp.x1, tmp.x1);
111 2579572 : burn = nburn > burn ? nburn : burn;
112 2579572 : buf_xor (outbuf, tmp.x64, c->u_ctr.ctr, GCRY_XTS_BLOCK_LEN);
113 :
114 2579572 : outbuf += GCRY_XTS_BLOCK_LEN;
115 2579572 : inbuf += GCRY_XTS_BLOCK_LEN;
116 2579572 : inbuflen -= GCRY_XTS_BLOCK_LEN;
117 2579572 : nblocks--;
118 :
119 : /* Generate next tweak. */
120 2579572 : xts_gfmul_byA (c->u_ctr.ctr, c->u_ctr.ctr);
121 : }
122 :
123 : /* Handle remaining data with ciphertext stealing. */
124 53518 : if (inbuflen)
125 : {
126 56 : if (!encrypt)
127 : {
128 28 : gcry_assert (inbuflen > GCRY_XTS_BLOCK_LEN);
129 28 : gcry_assert (inbuflen < GCRY_XTS_BLOCK_LEN * 2);
130 :
131 : /* Generate last tweak. */
132 28 : xts_gfmul_byA (tmp.x1, c->u_ctr.ctr);
133 :
134 : /* Decrypt last block first. */
135 28 : buf_xor (outbuf, inbuf, tmp.x64, GCRY_XTS_BLOCK_LEN);
136 28 : nburn = crypt_fn (&c->context.c, outbuf, outbuf);
137 28 : burn = nburn > burn ? nburn : burn;
138 28 : buf_xor (outbuf, outbuf, tmp.x64, GCRY_XTS_BLOCK_LEN);
139 :
140 28 : inbuflen -= GCRY_XTS_BLOCK_LEN;
141 28 : inbuf += GCRY_XTS_BLOCK_LEN;
142 28 : outbuf += GCRY_XTS_BLOCK_LEN;
143 : }
144 :
145 56 : gcry_assert (inbuflen < GCRY_XTS_BLOCK_LEN);
146 56 : outbuf -= GCRY_XTS_BLOCK_LEN;
147 :
148 : /* Steal ciphertext from previous block. */
149 56 : buf_cpy (tmp.x64, outbuf, GCRY_XTS_BLOCK_LEN);
150 56 : buf_cpy (tmp.x64, inbuf, inbuflen);
151 56 : buf_cpy (outbuf + GCRY_XTS_BLOCK_LEN, outbuf, inbuflen);
152 :
153 : /* Decrypt/Encrypt last block. */
154 56 : buf_xor (tmp.x64, tmp.x64, c->u_ctr.ctr, GCRY_XTS_BLOCK_LEN);
155 56 : nburn = crypt_fn (&c->context.c, tmp.x1, tmp.x1);
156 56 : burn = nburn > burn ? nburn : burn;
157 56 : buf_xor (outbuf, tmp.x64, c->u_ctr.ctr, GCRY_XTS_BLOCK_LEN);
158 : }
159 :
160 : /* Auto-increment data-unit sequence number */
161 53518 : xts_inc128 (c->u_iv.iv);
162 :
163 53518 : wipememory (&tmp, sizeof(tmp));
164 53518 : wipememory (c->u_ctr.ctr, sizeof(c->u_ctr.ctr));
165 :
166 53518 : if (burn > 0)
167 39292 : _gcry_burn_stack (burn + 4 * sizeof(void *));
168 :
169 53518 : return 0;
170 : }
|