/* * 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 = 0U; // The ipad constants have been XOR'd into the key and written to the // SHA-256 context. constexpr uint8_t HMAC_IPAD = 1U; // The opad constants have been XOR'd into the key and written to the // SHA-256 context. constexpr uint8_t HMAC_OPAD = 2U; // HMAC has been finalised constexpr uint8_t HMAC_FIN = 3U; // HMAC is in an invalid state. constexpr uint8_t HMAC_INVALID = 4U; static constexpr uint8_t ipad = 0x36U; static constexpr uint8_t opad = 0x5cU; HMAC::HMAC(const uint8_t *ik, uint32_t ikl) : hstate(HMAC_INIT), k{0U}, buf{0U} { std::fill(this->k, this->k+HMAC_KEY_LENGTH, 0); if (ikl < HMAC_KEY_LENGTH) { for (uint32_t i = 0U; i < ikl; i++) { this->k[i] = ik[i]; } while (ikl < HMAC_KEY_LENGTH) { this->k[ikl++] = 0U; } } else if (ikl > HMAC_KEY_LENGTH) { if (this->ctx.Update(ik, ikl) != EMSHAResult::OK) { this->hstate = HMAC_INVALID; } else if (this->ctx.Result(this->k) != EMSHAResult::OK) { this->hstate = HMAC_INVALID; } else if (this->ctx.Reset() != EMSHAResult::OK) { this->hstate = HMAC_INVALID; } else { this->hstate = HMAC_INIT; } } else { for (uint32_t i = 0U; i < ikl; i++) { this->k[i] = ik[i]; } } if (this->reset() != EMSHAResult::OK) { this->hstate = HMAC_INVALID; } } /* * A custom destructor is needed to ensure that the key material is wiped. */ HMAC::~HMAC() { (void)this->reset(); std::fill(this->k, this->k + HMAC_KEY_LENGTH, 0); } EMSHAResult HMAC::Reset() { return this->reset(); } EMSHAResult HMAC::reset() { EMSHAResult 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 (EMSHAResult::OK != 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 EMSHAResult::OK; } EMSHAResult HMAC::Update(const std::uint8_t *message, std::uint32_t messageLength) { EMSHAResult res; SHA256 &hctx = this->ctx; EMSHA_CHECK(message != nullptr, EMSHAResult::NullPointer); EMSHA_CHECK(HMAC_IPAD == this->hstate, EMSHAResult::InvalidState); // Write the message to the SHA-256 context. res = hctx.Update(message, messageLength); if (EMSHAResult::OK != res) { this->hstate = HMAC_INVALID; return res; } assert(HMAC_IPAD == this->hstate); return EMSHAResult::OK; } inline EMSHAResult HMAC::finalResult(uint8_t *d) { if (nullptr == d) { return EMSHAResult::NullPointer; } // If the HMAC has already been finalised, skip straight to // copying the result. if (this->hstate == HMAC_FIN) { std::copy(this->buf, this->buf+SHA256_HASH_SIZE, d); return EMSHAResult::OK; } EMSHA_CHECK(HMAC_IPAD == this->hstate, EMSHAResult::InvalidState); EMSHAResult res; // Use the result buffer as an intermediate buffer to store the result // of the inner hash. res = this->ctx.Result(this->buf); if (EMSHAResult::OK != res) { this->hstate = HMAC_INVALID; return EMSHAResult::InvalidState; } 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 (EMSHAResult::OK != 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 (EMSHAResult::OK != res) { this->hstate = HMAC_INVALID; return res; } // Write the outer hash result into the working buffer. res = this->ctx.Finalise(this->buf); if (EMSHAResult::OK != 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 EMSHAResult::OK; } EMSHAResult HMAC::Finalise(std::uint8_t *digest) { return this->finalResult(digest); } EMSHAResult HMAC::Result(std::uint8_t *digest) { return this->finalResult(digest); } std::uint32_t HMAC::Size() { return SHA256_HASH_SIZE; } EMSHAResult ComputeHMAC(const uint8_t *k, const uint32_t kl, const uint8_t *m, const uint32_t ml, uint8_t *d) { EMSHAResult res; HMAC h(k, kl); res = h.Update(m, ml); if (res == EMSHAResult::OK) { res = h.Result(d); } return res; } } // end of namespace emsha