Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F35336837
cipher-cmac.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
7 KB
Subscribers
None
cipher-cmac.c
View Options
/* cmac.c - CMAC, Cipher-based MAC.
* Copyright (C) 2013,2018 Jussi Kivilinna <jussi.kivilinna@iki.fi>
*
* This file is part of Libgcrypt.
*
* Libgcrypt is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* Libgcrypt is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include
<config.h>
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
"g10lib.h"
#include
"cipher.h"
#include
"cipher-internal.h"
#include
"bufhelp.h"
#define set_burn(burn, nburn) do { \
unsigned int __nburn = (nburn); \
(burn) = (burn) > __nburn ? (burn) : __nburn; } while (0)
gcry_err_code_t
_gcry_cmac_write
(
gcry_cipher_hd_t
c
,
gcry_cmac_context_t
*
ctx
,
const
byte
*
inbuf
,
size_t
inlen
)
{
gcry_cipher_encrypt_t
enc_fn
=
c
->
spec
->
encrypt
;
size_t
blocksize_shift
=
_gcry_blocksize_shift
(
c
);
size_t
blocksize
=
1
<<
blocksize_shift
;
byte
outbuf
[
MAX_BLOCKSIZE
];
unsigned
int
burn
=
0
;
unsigned
int
nblocks
;
size_t
n
;
if
(
ctx
->
tag
)
return
GPG_ERR_INV_STATE
;
if
(
!
inbuf
)
return
GPG_ERR_INV_ARG
;
if
(
inlen
==
0
)
return
0
;
/* Last block is needed for cmac_final. */
if
(
ctx
->
mac_unused
+
inlen
<=
blocksize
)
{
buf_cpy
(
&
ctx
->
macbuf
[
ctx
->
mac_unused
],
inbuf
,
inlen
);
ctx
->
mac_unused
+=
inlen
;
inbuf
+=
inlen
;
inlen
-=
inlen
;
return
0
;
}
if
(
ctx
->
mac_unused
)
{
n
=
inlen
;
if
(
n
>
blocksize
-
ctx
->
mac_unused
)
n
=
blocksize
-
ctx
->
mac_unused
;
buf_cpy
(
&
ctx
->
macbuf
[
ctx
->
mac_unused
],
inbuf
,
n
);
ctx
->
mac_unused
+=
n
;
inbuf
+=
n
;
inlen
-=
n
;
cipher_block_xor
(
ctx
->
u_iv
.
iv
,
ctx
->
u_iv
.
iv
,
ctx
->
macbuf
,
blocksize
);
set_burn
(
burn
,
enc_fn
(
&
c
->
context
.
c
,
ctx
->
u_iv
.
iv
,
ctx
->
u_iv
.
iv
));
ctx
->
mac_unused
=
0
;
}
if
(
c
->
bulk
.
cbc_enc
&&
inlen
>
blocksize
)
{
nblocks
=
inlen
>>
blocksize_shift
;
nblocks
-=
((
nblocks
<<
blocksize_shift
)
==
inlen
);
c
->
bulk
.
cbc_enc
(
&
c
->
context
.
c
,
ctx
->
u_iv
.
iv
,
outbuf
,
inbuf
,
nblocks
,
1
);
inbuf
+=
nblocks
<<
blocksize_shift
;
inlen
-=
nblocks
<<
blocksize_shift
;
wipememory
(
outbuf
,
sizeof
(
outbuf
));
}
else
while
(
inlen
>
blocksize
)
{
cipher_block_xor
(
ctx
->
u_iv
.
iv
,
ctx
->
u_iv
.
iv
,
inbuf
,
blocksize
);
set_burn
(
burn
,
enc_fn
(
&
c
->
context
.
c
,
ctx
->
u_iv
.
iv
,
ctx
->
u_iv
.
iv
));
inlen
-=
blocksize
;
inbuf
+=
blocksize
;
}
/* Make sure that last block is passed to cmac_final. */
if
(
inlen
==
0
)
BUG
();
n
=
inlen
;
if
(
n
>
blocksize
-
ctx
->
mac_unused
)
n
=
blocksize
-
ctx
->
mac_unused
;
buf_cpy
(
&
ctx
->
macbuf
[
ctx
->
mac_unused
],
inbuf
,
n
);
ctx
->
mac_unused
+=
n
;
inbuf
+=
n
;
inlen
-=
n
;
if
(
burn
)
_gcry_burn_stack
(
burn
+
4
*
sizeof
(
void
*
));
return
0
;
}
gcry_err_code_t
_gcry_cmac_generate_subkeys
(
gcry_cipher_hd_t
c
,
gcry_cmac_context_t
*
ctx
)
{
const
unsigned
int
blocksize
=
c
->
spec
->
blocksize
;
byte
rb
,
carry
,
t
,
bi
;
unsigned
int
burn
;
int
i
,
j
;
union
{
size_t
_aligned
;
byte
buf
[
MAX_BLOCKSIZE
];
}
u
;
/* Tell compiler that we require a cipher with a 64bit or 128 bit block
* length, to allow better optimization of this function. */
if
(
blocksize
>
16
||
blocksize
<
8
||
blocksize
&
(
8
-
1
))
return
GPG_ERR_INV_CIPHER_MODE
;
if
(
MAX_BLOCKSIZE
<
blocksize
)
BUG
();
/* encrypt zero block */
memset
(
u
.
buf
,
0
,
blocksize
);
burn
=
c
->
spec
->
encrypt
(
&
c
->
context
.
c
,
u
.
buf
,
u
.
buf
);
/* Currently supported blocksizes are 16 and 8. */
rb
=
blocksize
==
16
?
0x87
:
0x1B
/* blocksize == 8 */
;
for
(
j
=
0
;
j
<
2
;
j
++
)
{
/* Generate subkeys K1 and K2 */
carry
=
0
;
for
(
i
=
blocksize
-
1
;
i
>=
0
;
i
--
)
{
bi
=
u
.
buf
[
i
];
t
=
carry
|
(
bi
<<
1
);
carry
=
bi
>>
7
;
u
.
buf
[
i
]
=
t
&
0xff
;
ctx
->
subkeys
[
j
][
i
]
=
u
.
buf
[
i
];
}
u
.
buf
[
blocksize
-
1
]
^=
carry
?
rb
:
0
;
ctx
->
subkeys
[
j
][
blocksize
-
1
]
=
u
.
buf
[
blocksize
-
1
];
}
wipememory
(
&
u
,
sizeof
(
u
));
if
(
burn
)
_gcry_burn_stack
(
burn
+
4
*
sizeof
(
void
*
));
return
0
;
}
gcry_err_code_t
_gcry_cmac_final
(
gcry_cipher_hd_t
c
,
gcry_cmac_context_t
*
ctx
)
{
const
unsigned
int
blocksize
=
c
->
spec
->
blocksize
;
unsigned
int
count
=
ctx
->
mac_unused
;
unsigned
int
burn
;
byte
*
subkey
;
/* Tell compiler that we require a cipher with a 64bit or 128 bit block
* length, to allow better optimization of this function. */
if
(
blocksize
>
16
||
blocksize
<
8
||
blocksize
&
(
8
-
1
))
return
GPG_ERR_INV_CIPHER_MODE
;
if
(
count
==
blocksize
)
subkey
=
ctx
->
subkeys
[
0
];
/* K1 */
else
{
subkey
=
ctx
->
subkeys
[
1
];
/* K2 */
ctx
->
macbuf
[
count
++
]
=
0x80
;
while
(
count
<
blocksize
)
ctx
->
macbuf
[
count
++
]
=
0
;
}
cipher_block_xor
(
ctx
->
macbuf
,
ctx
->
macbuf
,
subkey
,
blocksize
);
cipher_block_xor
(
ctx
->
u_iv
.
iv
,
ctx
->
u_iv
.
iv
,
ctx
->
macbuf
,
blocksize
);
burn
=
c
->
spec
->
encrypt
(
&
c
->
context
.
c
,
ctx
->
u_iv
.
iv
,
ctx
->
u_iv
.
iv
);
if
(
burn
)
_gcry_burn_stack
(
burn
+
4
*
sizeof
(
void
*
));
ctx
->
mac_unused
=
0
;
return
0
;
}
static
gcry_err_code_t
cmac_tag
(
gcry_cipher_hd_t
c
,
gcry_cmac_context_t
*
ctx
,
unsigned
char
*
tag
,
size_t
taglen
,
int
check
)
{
gcry_err_code_t
ret
;
if
(
!
tag
||
taglen
==
0
||
taglen
>
c
->
spec
->
blocksize
)
return
GPG_ERR_INV_ARG
;
if
(
!
ctx
->
tag
)
{
ret
=
_gcry_cmac_final
(
c
,
ctx
);
if
(
ret
!=
0
)
return
ret
;
ctx
->
tag
=
1
;
}
if
(
!
check
)
{
memcpy
(
tag
,
ctx
->
u_iv
.
iv
,
taglen
);
return
GPG_ERR_NO_ERROR
;
}
else
{
return
buf_eq_const
(
tag
,
ctx
->
u_iv
.
iv
,
taglen
)
?
GPG_ERR_NO_ERROR
:
GPG_ERR_CHECKSUM
;
}
}
void
_gcry_cmac_reset
(
gcry_cmac_context_t
*
ctx
)
{
char
tmp_buf
[
sizeof
(
ctx
->
subkeys
)];
/* Only keep subkeys when reseting context. */
buf_cpy
(
tmp_buf
,
ctx
->
subkeys
,
sizeof
(
ctx
->
subkeys
));
memset
(
ctx
,
0
,
sizeof
(
*
ctx
));
buf_cpy
(
ctx
->
subkeys
,
tmp_buf
,
sizeof
(
ctx
->
subkeys
));
wipememory
(
tmp_buf
,
sizeof
(
tmp_buf
));
}
gcry_err_code_t
_gcry_cipher_cmac_authenticate
(
gcry_cipher_hd_t
c
,
const
unsigned
char
*
abuf
,
size_t
abuflen
)
{
if
(
abuflen
>
0
&&
!
abuf
)
return
GPG_ERR_INV_ARG
;
/* To support new blocksize, update cmac_generate_subkeys() then add new
blocksize here. */
if
(
c
->
spec
->
blocksize
!=
16
&&
c
->
spec
->
blocksize
!=
8
)
return
GPG_ERR_INV_CIPHER_MODE
;
return
_gcry_cmac_write
(
c
,
&
c
->
u_mode
.
cmac
,
abuf
,
abuflen
);
}
gcry_err_code_t
_gcry_cipher_cmac_get_tag
(
gcry_cipher_hd_t
c
,
unsigned
char
*
outtag
,
size_t
taglen
)
{
return
cmac_tag
(
c
,
&
c
->
u_mode
.
cmac
,
outtag
,
taglen
,
0
);
}
gcry_err_code_t
_gcry_cipher_cmac_check_tag
(
gcry_cipher_hd_t
c
,
const
unsigned
char
*
intag
,
size_t
taglen
)
{
return
cmac_tag
(
c
,
&
c
->
u_mode
.
cmac
,
(
unsigned
char
*
)
intag
,
taglen
,
1
);
}
gcry_err_code_t
_gcry_cipher_cmac_set_subkeys
(
gcry_cipher_hd_t
c
)
{
return
_gcry_cmac_generate_subkeys
(
c
,
&
c
->
u_mode
.
cmac
);
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Fri, Feb 6, 8:10 AM (10 h, 27 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
98/e6/50388113dfe9c1a861c13979c8a8
Attached To
rC libgcrypt
Event Timeline
Log In to Comment