Imported Upstream version 1.15.1
[platform/upstream/krb5.git] / src / lib / crypto / krb / cmac.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/crypto/krb/cmac.c */
3 /*
4  * Copyright 2010 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26
27 #include "crypto_int.h"
28
29 #define BLOCK_SIZE 16
30
31 static unsigned char const_Rb[BLOCK_SIZE] = {
32     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
33     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87
34 };
35
36 static void
37 xor_128(unsigned char *a, unsigned char *b, unsigned char *out)
38 {
39     int z;
40
41     for (z = 0; z < BLOCK_SIZE / 4; z++) {
42         unsigned char *aptr = &a[z * 4];
43         unsigned char *bptr = &b[z * 4];
44         unsigned char *outptr = &out[z * 4];
45
46         store_32_n(load_32_n(aptr) ^ load_32_n(bptr), outptr);
47     }
48 }
49
50 static void
51 leftshift_onebit(unsigned char *input, unsigned char *output)
52 {
53     int i;
54     unsigned char overflow = 0;
55
56     for (i = BLOCK_SIZE - 1; i >= 0; i--) {
57         output[i] = input[i] << 1;
58         output[i] |= overflow;
59         overflow = (input[i] & 0x80) ? 1 : 0;
60     }
61 }
62
63 /* Generate subkeys K1 and K2 as described in RFC 4493 figure 2.2. */
64 static krb5_error_code
65 generate_subkey(const struct krb5_enc_provider *enc,
66                 krb5_key key,
67                 unsigned char *K1,
68                 unsigned char *K2)
69 {
70     unsigned char L[BLOCK_SIZE];
71     unsigned char tmp[BLOCK_SIZE];
72     krb5_data d;
73     krb5_error_code ret;
74
75     /* L := encrypt(K, const_Zero) */
76     memset(L, 0, sizeof(L));
77     d = make_data(L, BLOCK_SIZE);
78     ret = encrypt_block(enc, key, &d);
79     if (ret != 0)
80         return ret;
81
82     /* K1 := (MSB(L) == 0) ? L << 1 : (L << 1) XOR const_Rb */
83     if ((L[0] & 0x80) == 0) {
84         leftshift_onebit(L, K1);
85     } else {
86         leftshift_onebit(L, tmp);
87         xor_128(tmp, const_Rb, K1);
88     }
89
90     /* K2 := (MSB(K1) == 0) ? K1 << 1 : (K1 << 1) XOR const_Rb */
91     if ((K1[0] & 0x80) == 0) {
92         leftshift_onebit(K1, K2);
93     } else {
94         leftshift_onebit(K1, tmp);
95         xor_128(tmp, const_Rb, K2);
96     }
97
98     return 0;
99 }
100
101 /* Pad out lastb with a 1 bit followed by 0 bits, placing the result in pad. */
102 static void
103 padding(unsigned char *lastb, unsigned char *pad, int length)
104 {
105     int j;
106
107     /* original last block */
108     for (j = 0; j < BLOCK_SIZE; j++) {
109         if (j < length) {
110             pad[j] = lastb[j];
111         } else if (j == length) {
112             pad[j] = 0x80;
113         } else {
114             pad[j] = 0x00;
115         }
116     }
117 }
118
119 /*
120  * Implementation of CMAC algorithm. When used with AES, this function
121  * is compatible with RFC 4493 figure 2.3.
122  */
123 krb5_error_code
124 krb5int_cmac_checksum(const struct krb5_enc_provider *enc, krb5_key key,
125                       const krb5_crypto_iov *data, size_t num_data,
126                       krb5_data *output)
127 {
128     unsigned char Y[BLOCK_SIZE], M_last[BLOCK_SIZE], padded[BLOCK_SIZE];
129     unsigned char K1[BLOCK_SIZE], K2[BLOCK_SIZE];
130     unsigned char input[BLOCK_SIZE];
131     unsigned int n, i, flag;
132     krb5_error_code ret;
133     struct iov_cursor cursor;
134     size_t length;
135     krb5_crypto_iov iov[1];
136     krb5_data d;
137
138     assert(enc->cbc_mac != NULL);
139
140     if (enc->block_size != BLOCK_SIZE)
141         return KRB5_BAD_MSIZE;
142
143     length = iov_total_length(data, num_data, TRUE);
144
145     /* Step 1. */
146     ret = generate_subkey(enc, key, K1, K2);
147     if (ret != 0)
148         return ret;
149
150     /* Step 2. */
151     n = (length + BLOCK_SIZE - 1) / BLOCK_SIZE;
152
153     /* Step 3. */
154     if (n == 0) {
155         n = 1;
156         flag = 0;
157     } else {
158         flag = ((length % BLOCK_SIZE) == 0);
159     }
160
161     iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
162     iov[0].data = make_data(input, BLOCK_SIZE);
163
164     /* Step 5 (we'll do step 4 in a bit). */
165     memset(Y, 0, BLOCK_SIZE);
166     d = make_data(Y, BLOCK_SIZE);
167
168     /* Step 6 (all but last block). */
169     k5_iov_cursor_init(&cursor, data, num_data, BLOCK_SIZE, TRUE);
170     for (i = 0; i < n - 1; i++) {
171         k5_iov_cursor_get(&cursor, input);
172
173         ret = enc->cbc_mac(key, iov, 1, &d, &d);
174         if (ret != 0)
175             return ret;
176     }
177
178     /* Step 4. */
179     k5_iov_cursor_get(&cursor, input);
180     if (flag) {
181         /* last block is complete block */
182         xor_128(input, K1, M_last);
183     } else {
184         padding(input, padded, length % BLOCK_SIZE);
185         xor_128(padded, K2, M_last);
186     }
187
188     /* Step 6 (last block). */
189     iov[0].data = make_data(M_last, BLOCK_SIZE);
190     ret = enc->cbc_mac(key, iov, 1, &d, &d);
191     if (ret != 0)
192         return ret;
193
194     assert(output->length >= d.length);
195
196     output->length = d.length;
197     memcpy(output->data, d.data, d.length);
198
199     return 0;
200 }