1 #include "integrity_algo.hpp"
2 
3 #include "message_parsers.hpp"
4 
5 #include <openssl/evp.h>
6 #include <openssl/hmac.h>
7 #include <openssl/sha.h>
8 
9 namespace cipher
10 {
11 
12 namespace integrity
13 {
14 
15 AlgoSHA1::AlgoSHA1(const std::vector<uint8_t>& sik) :
16     Interface(SHA1_96_AUTHCODE_LENGTH)
17 {
18     k1 = generateKn(sik, rmcp::const_1);
19 }
20 
21 std::vector<uint8_t> AlgoSHA1::generateHMAC(const uint8_t* input,
22                                             const size_t len) const
23 {
24     std::vector<uint8_t> output(SHA_DIGEST_LENGTH);
25     unsigned int mdLen = 0;
26 
27     if (HMAC(EVP_sha1(), k1.data(), k1.size(), input, len, output.data(),
28              &mdLen) == NULL)
29     {
30         throw std::runtime_error("Generating integrity data failed");
31     }
32 
33     // HMAC generates Message Digest to the size of SHA_DIGEST_LENGTH, the
34     // AuthCode field length is based on the integrity algorithm. So we are
35     // interested only in the AuthCode field length of the generated Message
36     // digest.
37     output.resize(authCodeLength);
38 
39     return output;
40 }
41 
42 bool AlgoSHA1::verifyIntegrityData(
43     const std::vector<uint8_t>& packet, const size_t length,
44     std::vector<uint8_t>::const_iterator integrityDataBegin,
45     std::vector<uint8_t>::const_iterator integrityDataEnd) const
46 {
47     auto output = generateHMAC(
48         packet.data() + message::parser::RMCP_SESSION_HEADER_SIZE, length);
49 
50     // Verify if the generated integrity data for the packet and the received
51     // integrity data matches.
52     return (std::equal(output.begin(), output.end(), integrityDataBegin,
53                        integrityDataEnd));
54 }
55 
56 std::vector<uint8_t>
57     AlgoSHA1::generateIntegrityData(const std::vector<uint8_t>& packet) const
58 {
59     return generateHMAC(
60         packet.data() + message::parser::RMCP_SESSION_HEADER_SIZE,
61         packet.size() - message::parser::RMCP_SESSION_HEADER_SIZE);
62 }
63 
64 std::vector<uint8_t> AlgoSHA1::generateKn(const std::vector<uint8_t>& sik,
65                                           const rmcp::Const_n& const_n) const
66 {
67     unsigned int mdLen = 0;
68     std::vector<uint8_t> Kn(sik.size());
69 
70     // Generated Kn for the integrity algorithm with the additional key keyed
71     // with SIK.
72     if (HMAC(EVP_sha1(), sik.data(), sik.size(), const_n.data(), const_n.size(),
73              Kn.data(), &mdLen) == NULL)
74     {
75         throw std::runtime_error("Generating KeyN for integrity "
76                                  "algorithm failed");
77     }
78     return Kn;
79 }
80 
81 AlgoSHA256::AlgoSHA256(const std::vector<uint8_t>& sik) :
82     Interface(SHA256_128_AUTHCODE_LENGTH)
83 {
84     k1 = generateKn(sik, rmcp::const_1);
85 }
86 
87 std::vector<uint8_t> AlgoSHA256::generateHMAC(const uint8_t* input,
88                                               const size_t len) const
89 {
90     std::vector<uint8_t> output(SHA256_DIGEST_LENGTH);
91     unsigned int mdLen = 0;
92 
93     if (HMAC(EVP_sha256(), k1.data(), k1.size(), input, len, output.data(),
94              &mdLen) == NULL)
95     {
96         throw std::runtime_error("Generating HMAC_SHA256_128 failed");
97     }
98 
99     // HMAC generates Message Digest to the size of SHA256_DIGEST_LENGTH, the
100     // AuthCode field length is based on the integrity algorithm. So we are
101     // interested only in the AuthCode field length of the generated Message
102     // digest.
103     output.resize(authCodeLength);
104 
105     return output;
106 }
107 
108 bool AlgoSHA256::verifyIntegrityData(
109     const std::vector<uint8_t>& packet, const size_t length,
110     std::vector<uint8_t>::const_iterator integrityDataBegin,
111     std::vector<uint8_t>::const_iterator integrityDataEnd) const
112 {
113     auto output = generateHMAC(
114         packet.data() + message::parser::RMCP_SESSION_HEADER_SIZE, length);
115 
116     // Verify if the generated integrity data for the packet and the received
117     // integrity data matches.
118     return (std::equal(output.begin(), output.end(), integrityDataBegin,
119                        integrityDataEnd));
120 }
121 
122 std::vector<uint8_t>
123     AlgoSHA256::generateIntegrityData(const std::vector<uint8_t>& packet) const
124 {
125     return generateHMAC(
126         packet.data() + message::parser::RMCP_SESSION_HEADER_SIZE,
127         packet.size() - message::parser::RMCP_SESSION_HEADER_SIZE);
128 }
129 
130 std::vector<uint8_t> AlgoSHA256::generateKn(const std::vector<uint8_t>& sik,
131                                             const rmcp::Const_n& const_n) const
132 {
133     unsigned int mdLen = 0;
134     std::vector<uint8_t> Kn(sik.size());
135 
136     // Generated Kn for the integrity algorithm with the additional key keyed
137     // with SIK.
138     if (HMAC(EVP_sha256(), sik.data(), sik.size(), const_n.data(),
139              const_n.size(), Kn.data(), &mdLen) == NULL)
140     {
141         throw std::runtime_error("Generating KeyN for integrity "
142                                  "algorithm HMAC_SHA256 failed");
143     }
144     return Kn;
145 }
146 
147 } // namespace integrity
148 
149 } // namespace cipher
150