summaryrefslogtreecommitdiffstats
path: root/integrity_algo.hpp
blob: 6c8460f741cbe40dfc085f392f89d9b455808a7f (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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
#pragma once

#include "rmcp.hpp"

#include <array>
#include <vector>

namespace cipher
{

namespace integrity
{

/**
 * @enum Integrity Algorithms
 *
 * The Integrity Algorithm Number specifies the algorithm used to generate the
 * contents for the AuthCode “signature” field that accompanies authenticated
 * IPMI v2.0/RMCP+ messages once the session has been established. If the
 * Integrity Algorithm is none the AuthCode value is not calculated and the
 * AuthCode field in the message is not present. Based on security
 * recommendations NONE will not be supported.
 */
enum class Algorithms : uint8_t
{
    NONE,            // Mandatory (implemented, not supported)
    HMAC_SHA1_96,    // Mandatory (implemented, default choice in ipmitool)
    HMAC_MD5_128,    // Optional (not implemented)
    MD5_128,         // Optional (not implemented)
    HMAC_SHA256_128, // Optional (implemented, best available)
};

/**
 * @class Interface
 *
 * Interface is the base class for the Integrity Algorithms.
 * Unless otherwise specified, the integrity algorithm is applied to the packet
 * data starting with the AuthType/Format field up to and including the field
 * that immediately precedes the AuthCode field itself.
 */
class Interface
{
  public:
    /**
     * @brief Constructor for Interface
     *
     * @param[in] - AuthCode length
     */
    explicit Interface(size_t authLength) : authCodeLength(authLength)
    {
    }

    Interface() = delete;
    virtual ~Interface() = default;
    Interface(const Interface&) = default;
    Interface& operator=(const Interface&) = default;
    Interface(Interface&&) = default;
    Interface& operator=(Interface&&) = default;

    /**
     * @brief Verify the integrity data of the packet
     *
     * @param[in] packet - Incoming IPMI packet
     * @param[in] packetLen - Packet length excluding authCode
     * @param[in] integrityData - Iterator to the authCode in the packet
     *
     * @return true if authcode in the packet is equal to one generated
     *         using integrity algorithm on the packet data, false otherwise
     */
    bool virtual verifyIntegrityData(
        const std::vector<uint8_t>& packet, const size_t packetLen,
        std::vector<uint8_t>::const_iterator integrityData) const = 0;

    /**
     * @brief Generate integrity data for the outgoing IPMI packet
     *
     * @param[in] input - Outgoing IPMI packet
     *
     * @return authcode for the outgoing IPMI packet
     *
     */
    std::vector<uint8_t> virtual generateIntegrityData(
        const std::vector<uint8_t>& input) const = 0;

    /**
     * @brief Check if the Integrity algorithm is supported
     *
     * @param[in] algo - integrity algorithm
     *
     * @return true if algorithm is supported else false
     *
     */
    static bool isAlgorithmSupported(Algorithms algo)
    {
        if (algo == Algorithms::HMAC_SHA1_96 ||
            algo == Algorithms::HMAC_SHA256_128)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    /**
     * @brief Generate additional keying material based on SIK
     *
     * @note
     * The IPMI 2.0 spec only states that the additional keying material is
     * generated by running HMAC(constN) using SIK as the key. It does not
     * state whether this is the integrity algorithm or the authentication
     * algorithm. Other implementations of the RMCP+ algorithm (ipmitool
     * and ipmiutil) are not consistent on this matter. But it does not
     * really matter because based on any of the defined cipher suites, the
     * integrity and authentication algorithms are both based on the same
     * digest method (integrity::Algorithms::HMAC_SHA1_96 uses SHA1 and
     * rakp_auth::Algorithms::RAKP_HMAC_SHA1 uses SHA1). None of the
     * defined cipher suites mix and match digests for integrity and
     * authentication. Generating Kn belongs in either the integrity or
     * authentication classes, so in this implementation, integrity has
     * been chosen.
     *
     * @param[in] sik - session integrity key
     * @param[in] data - 20-byte Const_n
     *
     * @return on success returns the Kn based on this integrity class
     *
     */
    std::vector<uint8_t> virtual generateKn(
        const std::vector<uint8_t>& sik, const rmcp::Const_n& data) const = 0;

    /** @brief Authcode field
     *
     *  AuthCode field length varies based on the integrity algorithm, for
     *  HMAC-SHA1-96 the authcode field is 12 bytes. For HMAC-SHA256-128 and
     *  HMAC-MD5-128 the authcode field is 16 bytes.
     */
    size_t authCodeLength;

  protected:
    /** @brief K1 key used to generated the integrity data. */
    std::vector<uint8_t> k1;
};

/**
 * @class AlgoSHA1
 *
 * @brief Implementation of the HMAC-SHA1-96 Integrity algorithm
 *
 * HMAC-SHA1-96 take the Session Integrity Key and use it to generate K1. K1 is
 * then used as the key for use in HMAC to produce the AuthCode field.
 * For “one-key” logins, the user’s key (password) is used in the creation of
 * the Session Integrity Key. When the HMAC-SHA1-96 Integrity Algorithm is used
 * the resulting AuthCode field is 12 bytes (96 bits).
 */
class AlgoSHA1 final : public Interface
{
  public:
    static constexpr size_t SHA1_96_AUTHCODE_LENGTH = 12;

    /**
     * @brief Constructor for AlgoSHA1
     *
     * @param[in] - Session Integrity Key
     */
    explicit AlgoSHA1(const std::vector<uint8_t>& sik);

    AlgoSHA1() = delete;
    ~AlgoSHA1() = default;
    AlgoSHA1(const AlgoSHA1&) = default;
    AlgoSHA1& operator=(const AlgoSHA1&) = default;
    AlgoSHA1(AlgoSHA1&&) = default;
    AlgoSHA1& operator=(AlgoSHA1&&) = default;

    /**
     * @brief Verify the integrity data of the packet
     *
     * @param[in] packet - Incoming IPMI packet
     * @param[in] length - Length of the data in the packet to calculate
     *                     the integrity data
     * @param[in] integrityData - Iterator to the authCode in the packet
     *
     * @return true if authcode in the packet is equal to one generated
     *         using integrity algorithm on the packet data, false otherwise
     */
    bool verifyIntegrityData(
        const std::vector<uint8_t>& packet, const size_t length,
        std::vector<uint8_t>::const_iterator integrityData) const override;

    /**
     * @brief Generate integrity data for the outgoing IPMI packet
     *
     * @param[in] input - Outgoing IPMI packet
     *
     * @return on success return the integrity data for the outgoing IPMI
     *         packet
     */
    std::vector<uint8_t> generateIntegrityData(
        const std::vector<uint8_t>& packet) const override;

    /**
     * @brief Generate additional keying material based on SIK
     *
     * @param[in] sik - session integrity key
     * @param[in] data - 20-byte Const_n
     *
     * @return on success returns the Kn based on HMAC-SHA1
     *
     */
    std::vector<uint8_t> generateKn(const std::vector<uint8_t>& sik,
                                    const rmcp::Const_n& const_n) const;

  private:
    /**
     * @brief Generate HMAC based on HMAC-SHA1-96 algorithm
     *
     * @param[in] input - pointer to the message
     * @param[in] length - length of the message
     *
     * @return on success returns the message authentication code
     *
     */
    std::vector<uint8_t> generateHMAC(const uint8_t* input,
                                      const size_t len) const;
};

/**
 * @class AlgoSHA256
 *
 * @brief Implementation of the HMAC-SHA256-128 Integrity algorithm
 *
 * HMAC-SHA256-128 take the Session Integrity Key and use it to generate K1. K1
 * is then used as the key for use in HMAC to produce the AuthCode field.  For
 * “one-key” logins, the user’s key (password) is used in the creation of the
 * Session Integrity Key. When the HMAC-SHA256-128 Integrity Algorithm is used
 * the resulting AuthCode field is 16 bytes (128 bits).
 */
class AlgoSHA256 final : public Interface
{
  public:
    static constexpr size_t SHA256_128_AUTHCODE_LENGTH = 16;

    /**
     * @brief Constructor for AlgoSHA256
     *
     * @param[in] - Session Integrity Key
     */
    explicit AlgoSHA256(const std::vector<uint8_t>& sik);

    AlgoSHA256() = delete;
    ~AlgoSHA256() = default;
    AlgoSHA256(const AlgoSHA256&) = default;
    AlgoSHA256& operator=(const AlgoSHA256&) = default;
    AlgoSHA256(AlgoSHA256&&) = default;
    AlgoSHA256& operator=(AlgoSHA256&&) = default;

    /**
     * @brief Verify the integrity data of the packet
     *
     * @param[in] packet - Incoming IPMI packet
     * @param[in] length - Length of the data in the packet to calculate
     *                     the integrity data
     * @param[in] integrityData - Iterator to the authCode in the packet
     *
     * @return true if authcode in the packet is equal to one generated
     *         using integrity algorithm on the packet data, false otherwise
     */
    bool verifyIntegrityData(
        const std::vector<uint8_t>& packet, const size_t length,
        std::vector<uint8_t>::const_iterator integrityData) const override;

    /**
     * @brief Generate integrity data for the outgoing IPMI packet
     *
     * @param[in] packet - Outgoing IPMI packet
     *
     * @return on success return the integrity data for the outgoing IPMI
     *         packet
     */
    std::vector<uint8_t> generateIntegrityData(
        const std::vector<uint8_t>& packet) const override;

    /**
     * @brief Generate additional keying material based on SIK
     *
     * @param[in] sik - session integrity key
     * @param[in] data - 20-byte Const_n
     *
     * @return on success returns the Kn based on HMAC-SHA256
     *
     */
    std::vector<uint8_t> generateKn(const std::vector<uint8_t>& sik,
                                    const rmcp::Const_n& const_n) const;

  private:
    /**
     * @brief Generate HMAC based on HMAC-SHA256-128 algorithm
     *
     * @param[in] input - pointer to the message
     * @param[in] len - length of the message
     *
     * @return on success returns the message authentication code
     *
     */
    std::vector<uint8_t> generateHMAC(const uint8_t* input,
                                      const size_t len) const;
};

} // namespace integrity

} // namespace cipher
OpenPOWER on IntegriCloud