summaryrefslogtreecommitdiffstats
path: root/comm/third_party/botan/src/lib/mac/gmac/gmac.cpp
blob: 6b162857f3f614dbd9a69a73e2e730127dbe2de8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/*
 * GMAC
 * (C) 2016 Matthias Gierlings, René Korthaus
 * (C) 2017 Jack Lloyd
 *
 * Botan is released under the Simplified BSD License (see license.txt)
 */

#include <botan/gmac.h>
#include <botan/ghash.h>
#include <botan/exceptn.h>
#include <botan/block_cipher.h>

namespace Botan {

GMAC::GMAC(BlockCipher* cipher) :
   m_cipher(cipher),
   m_ghash(new GHASH),
   m_aad_buf(GCM_BS),
   m_aad_buf_pos(0),
   m_initialized(false)
   {
   }

void GMAC::clear()
   {
   m_cipher->clear();
   m_ghash->clear();
   zeroise(m_aad_buf);
   m_aad_buf_pos = 0;
   m_initialized = false;
   }

GMAC::~GMAC() { /* for unique_ptr */ }

Key_Length_Specification GMAC::key_spec() const
   {
   return m_cipher->key_spec();
   }

std::string GMAC::name() const
   {
   return "GMAC(" + m_cipher->name() + ")";
   }

size_t GMAC::output_length() const
   {
   return GCM_BS;
   }

void GMAC::add_data(const uint8_t input[], size_t size)
   {
   if(m_aad_buf_pos > 0)
      {
      const size_t taking = std::min(GCM_BS - m_aad_buf_pos, size);
      copy_mem(&m_aad_buf[m_aad_buf_pos], input, taking);
      m_aad_buf_pos += taking;
      input += taking;
      size -= taking;

      if(m_aad_buf_pos == GCM_BS)
         {
         m_ghash->update_associated_data(m_aad_buf.data(), GCM_BS);
         m_aad_buf_pos = 0;
         }
      }

   const size_t left_over = size % GCM_BS;
   const size_t full_blocks = size - left_over;
   m_ghash->update_associated_data(input, full_blocks);
   input += full_blocks;

   if(left_over > 0)
      {
      copy_mem(&m_aad_buf[m_aad_buf_pos], input, left_over);
      m_aad_buf_pos += left_over;
      }
   }

void GMAC::key_schedule(const uint8_t key[], size_t size)
   {
   clear();
   m_cipher->set_key(key, size);

   secure_vector<uint8_t> H(GCM_BS);
   m_cipher->encrypt(H);
   m_ghash->set_key(H);
   }

void GMAC::start_msg(const uint8_t nonce[], size_t nonce_len)
   {
   secure_vector<uint8_t> y0(GCM_BS);

   if(nonce_len == 12)
      {
      copy_mem(y0.data(), nonce, nonce_len);
      y0[GCM_BS - 1] = 1;
      }
   else
      {
      m_ghash->ghash_update(y0, nonce, nonce_len);
      m_ghash->add_final_block(y0, 0, nonce_len);
      }

   secure_vector<uint8_t> m_enc_y0(GCM_BS);
   m_cipher->encrypt(y0.data(), m_enc_y0.data());
   m_ghash->start(m_enc_y0.data(), m_enc_y0.size());
   m_initialized = true;
   }

void GMAC::final_result(uint8_t mac[])
   {
   // This ensures the GMAC computation has been initialized with a fresh
   // nonce. The aim of this check is to prevent developers from re-using
   // nonces (and potential nonce-reuse attacks).
   if(m_initialized == false)
      throw Invalid_State("GMAC was not used with a fresh nonce");

   // process the rest of the aad buffer. Even if it is a partial block only
   // ghash_update will process it properly.
   if(m_aad_buf_pos > 0)
       {
       m_ghash->update_associated_data(m_aad_buf.data(), m_aad_buf_pos);
       }

   m_ghash->final(mac, output_length());
   clear();
   }

MessageAuthenticationCode* GMAC::clone() const
   {
   return new GMAC(m_cipher->clone());
   }
}