/* * The MIT License (MIT) * * Copyright (c) 2015 K. Isom * * Permission is hereby granted, free of charge, to any person obtaining a copy * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include namespace emsha { // These constants are used to keep track of the state of the HMAC. // HMAC is in a clean-slate state following a call to Reset(). constexpr uint8_t HMAC_INIT = 0; // The ipad constants have been XOR'd into the key and written to the // SHA-256 context. constexpr uint8_t HMAC_IPAD = 1; // The opad constants have been XOR'd into the key and written to the // SHA-256 context. constexpr uint8_t HMAC_OPAD = 2; // HMAC has been finalised constexpr uint8_t HMAC_FIN = 3; // HMAC is in an invalid state. constexpr uint8_t HMAC_INVALID = 4; static constexpr uint8_t ipad = 0x36; static constexpr uint8_t opad = 0x5c; HMAC::HMAC(const uint8_t *ik, uint32_t ikl) : hstate(HMAC_INIT), k{0}, buf{0} { std::fill(this->k, this->k + emsha::HMAC_KEY_LENGTH, 0); if (ikl < HMAC_KEY_LENGTH) { std::copy(ik, ik + ikl, this->k); while (ikl < HMAC_KEY_LENGTH) { this->k[ikl++] = 0; } } else if (ikl > HMAC_KEY_LENGTH) { this->ctx.Update(ik, ikl); this->ctx.Result(this->k); this->ctx.Reset(); } else { std::copy(ik, ik + ikl, this->k); } this->reset(); } /* * A custom destructor is needed to ensure that the key material is wiped. */ HMAC::~HMAC() { this->reset(); std::fill(this->k, this->k + HMAC_KEY_LENGTH, 0); } EMSHA_RESULT HMAC::Reset() { return this->reset(); } EMSHA_RESULT HMAC::reset() { EMSHA_RESULT res; // Following a reset, both SHA-256 contexts and result buffer should be // zero'd out for a clean slate. The HMAC state should be reset // accordingly. this->ctx.Reset(); std::fill(this->buf, this->buf + SHA256_HASH_SIZE, 0); // Set up the k0 ⊕ ipad construction, and write it into the // SHA-256 context. uint8_t key[HMAC_KEY_LENGTH]; for (uint32_t i = 0; i < HMAC_KEY_LENGTH; i++) { key[i] = this->k[i] ^ ipad; } res = this->ctx.Update(key, HMAC_KEY_LENGTH); if (EMSHA_ROK != res) { this->hstate = HMAC_INVALID; return res; } // This key is considered sensitive material and should be wiped. std::fill(key, key + HMAC_KEY_LENGTH, 0); this->hstate = HMAC_IPAD; return EMSHA_ROK; } EMSHA_RESULT HMAC::Update(const uint8_t *m, uint32_t ml) { EMSHA_RESULT res; SHA256 &hctx = this->ctx; EMSHA_CHECK(m != nullptr, EMSHA_NULLPTR); EMSHA_CHECK(HMAC_IPAD == this->hstate, EMSHA_INVALID_STATE); // Write the message to the SHA-256 context. res = hctx.Update(m, ml); if (EMSHA_ROK != res) { this->hstate = HMAC_INVALID; return res; } assert(HMAC_IPAD == this->hstate); return EMSHA_ROK; } inline EMSHA_RESULT HMAC::finalResult(uint8_t *d) { if (nullptr == d) { return EMSHA_NULLPTR; } // If the HMAC has already been finalised, skip straight to // copying the result. if (HMAC_FIN == this->hstate) { std::copy(this->buf, this->buf + SHA256_HASH_SIZE, d); return EMSHA_ROK; } EMSHA_CHECK(HMAC_IPAD == this->hstate, EMSHA_INVALID_STATE); EMSHA_RESULT res; // Use the result buffer as an intermediate buffer to store the result // of the inner hash. res = this->ctx.Result(this->buf); if (EMSHA_ROK != res) { this->hstate = HMAC_INVALID; return EMSHA_INVALID_STATE; } assert(HMAC_IPAD == this->hstate); // The SHA-256 context needs to be reset so that it may be // re-used for the outer digest. this->ctx.Reset(); // Set up the k0 ⊕ opad construction, and write it into the // SHA-256 context. uint8_t key[HMAC_KEY_LENGTH]; for (uint32_t i = 0; i < HMAC_KEY_LENGTH; i++) { key[i] = this->k[i] ^ opad; } res = this->ctx.Update(key, HMAC_KEY_LENGTH); if (EMSHA_ROK != res) { this->hstate = HMAC_INVALID; return res; } this->hstate = HMAC_OPAD; // This key is considered sensitive material and should be wiped. std::fill(key, key + HMAC_KEY_LENGTH, 0); // Write the inner hash result into the outer hash. res = this->ctx.Update(this->buf, SHA256_HASH_SIZE); if (EMSHA_ROK != res) { this->hstate = HMAC_INVALID; return res; } // Write the outer hash result into the working buffer. res = this->ctx.Finalise(this->buf); if (EMSHA_ROK != res) { this->hstate = HMAC_INVALID; return res; } assert(HMAC_OPAD == this->hstate); std::copy(this->buf, this->buf + SHA256_HASH_SIZE, d); this->hstate = HMAC_FIN; return EMSHA_ROK; } EMSHA_RESULT HMAC::Finalise(uint8_t *d) { return this->finalResult(d); } EMSHA_RESULT HMAC::Result(uint8_t *d) { return this->finalResult(d); } std::uint32_t HMAC::Size() { return SHA256_HASH_SIZE; } EMSHA_RESULT ComputeHMAC(const uint8_t *k, uint32_t kl, const uint8_t *m, uint32_t ml, uint8_t *d) { EMSHA_RESULT res; HMAC h(k, kl); res = h.Update(m, ml); if (EMSHA_ROK != res) { return res; } res = h.Result(d); if (EMSHA_ROK != res) { return res; } return res; } } // end of namespace emsha