blob: 1389e28df69c0977d32b092dc8cfd1e47084bb58 [file] [log] [blame]
// Copyright 2021 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once
#include <cstdint>
#include "pw_bytes/span.h"
#include "pw_crypto/sha256_backend.h"
#include "pw_log/log.h"
#include "pw_status/status.h"
#include "pw_status/try.h"
#include "pw_stream/stream.h"
namespace pw::crypto::sha256 {
// Size in bytes of a SHA256 digest.
constexpr uint32_t kDigestSizeBytes = 32;
// State machine of a hashing session.
enum class Sha256State {
// Initialized and accepting input (via Update()).
kReady = 1,
// Finalized by Final(). Any additional requests, Update() or Final(), will
// trigger a transition to kError.
kFinalized = 2,
// In an unrecoverable error state.
kError = 3,
};
namespace backend {
// Primitive operations to be implemented by backends.
Status DoInit(NativeSha256Context& ctx);
Status DoUpdate(NativeSha256Context& ctx, ConstByteSpan data);
Status DoFinal(NativeSha256Context& ctx, ByteSpan out_digest);
} // namespace backend
// Sha256 computes the SHA256 digest of potentially long, non-contiguous input
// messages.
//
// Usage:
//
// if (!Sha256().Update(message).Update(more_message).Final(out_digest).ok()) {
// // Error handling.
// }
class Sha256 {
public:
Sha256() {
if (!backend::DoInit(native_ctx_).ok()) {
PW_LOG_DEBUG("backend::DoInit() failed");
state_ = Sha256State::kError;
return;
}
state_ = Sha256State::kReady;
}
// Update feeds `data` to the running hasher. The feeding can involve zero
// or more `Update()` calls and the order matters.
Sha256& Update(ConstByteSpan data) {
if (state_ != Sha256State::kReady) {
PW_LOG_DEBUG("The backend is not ready/initialized");
return *this;
}
if (!backend::DoUpdate(native_ctx_, data).ok()) {
PW_LOG_DEBUG("backend::DoUpdate() failed");
state_ = Sha256State::kError;
return *this;
}
return *this;
}
// Final wraps up the hashing session and outputs the final digest in the
// first `kDigestSizeBytes` of `out_digest`. `out_digest` must be at least
// `kDigestSizeBytes` long.
//
// Final locks down the Sha256 instance from any additional use.
//
// Any error, including those occurr inside `Init()` or `Update()` will be
// reflected in the return value of Final();
Status Final(ByteSpan out_digest) {
if (out_digest.size() < kDigestSizeBytes) {
PW_LOG_DEBUG("Digest output buffer is too small");
state_ = Sha256State::kError;
return Status::InvalidArgument();
}
if (state_ != Sha256State::kReady) {
PW_LOG_DEBUG("The backend is not ready/initialized");
return Status::FailedPrecondition();
}
auto status = backend::DoFinal(native_ctx_, out_digest);
if (!status.ok()) {
PW_LOG_DEBUG("backend::DoFinal() failed");
state_ = Sha256State::kError;
return status;
}
state_ = Sha256State::kFinalized;
return OkStatus();
}
private:
// Common hasher state. Tracked by the front-end.
Sha256State state_;
// Backend-specific context.
backend::NativeSha256Context native_ctx_;
};
// Hash calculates the SHA256 digest of `message` and stores the result
// in `out_digest`. `out_digest` must be at least `kDigestSizeBytes` long.
inline Status Hash(ConstByteSpan message, ByteSpan out_digest) {
return Sha256().Update(message).Final(out_digest);
}
inline Status Hash(stream::Reader& reader, ByteSpan out_digest) {
if (out_digest.size() < kDigestSizeBytes) {
return Status::InvalidArgument();
}
Sha256 sha256;
while (true) {
Result<ByteSpan> res = reader.Read(out_digest);
if (res.status().IsOutOfRange()) {
break;
}
PW_TRY(res.status());
sha256.Update(res.value());
}
return sha256.Final(out_digest);
}
} // namespace pw::crypto::sha256