• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2025 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #pragma once
16 
17 #include "pw_crypto/aes.h"
18 
19 namespace pw::crypto::aes_cmac {
20 
21 /// Computes the message authentication code (MAC) of a message using AES-CMAC.
22 ///
23 /// The interface specifically allows computing the MAC of potentially long,
24 /// non-contiguous messages. A MAC is similar to a message digest in that it can
25 /// be used to verify integrity, but since it also takes a secret `key` as input
26 /// it can also be used to verify authenticity, as the other party must also
27 /// know the secret key to compute the same MAC.
28 ///
29 /// Usage:
30 ///
31 /// @code{.cpp}
32 /// if (!Cmac(key).Update(part1).Update(part2).Final(out_mac).ok()) {
33 ///     // Error handling.
34 /// }
35 /// @endcode
36 class Cmac {
37  private:
38   enum class State {
39     /// Initialized and accepting input (via `Update()`).
40     kReady = 1,
41     /// Finalized by `Final()`. Any additional requests to `Update()` or
42     /// `Final()` will trigger a transition to `kError`.
43     kFinalized = 2,
44     /// In an unrecoverable error state.
45     kError = 3,
46   };
47 
48  public:
49   /// Initialize a `Cmac` instance using the specified `key`.
50   ///
51   /// @note Any error during initialization will be reflected in the return
52   /// value of `Final()`.
53   ///
54   /// @param[in] key A byte string containing the key to used in the CMAC
55   /// operation.
56   template <size_t key_size>
Cmac(span<const std::byte,key_size> key)57   explicit Cmac(span<const std::byte, key_size> key) {
58     constexpr auto this_op = pw::crypto::aes::backend::AesOperation::kCmac;
59     static_assert(pw::crypto::aes::internal::BackendSupports<this_op>(key_size),
60                   "Usupported key size for CMAC for backend.");
61 
62     if (!pw::crypto::aes::backend::DoInit(native_ctx_, key).ok()) {
63       PW_LOG_DEBUG("backend::DoInit() failed");
64       state_ = State::kError;
65       return;
66     }
67 
68     state_ = State::kReady;
69   }
70 
Cmac(span<const std::byte,dynamic_extent> key)71   explicit Cmac(span<const std::byte, dynamic_extent> key) {
72     constexpr auto this_op = pw::crypto::aes::backend::AesOperation::kCmac;
73     PW_ASSERT(pw::crypto::aes::internal::BackendSupports<this_op>(key.size()));
74 
75     if (!pw::crypto::aes::backend::DoInit(native_ctx_, key).ok()) {
76       PW_LOG_DEBUG("backend::DoInit() failed");
77       state_ = State::kError;
78       return;
79     }
80 
81     state_ = State::kReady;
82   }
83 
84   // Overload to enable implicit conversions to span if `T` is implicitly
85   // convertible to `span`.
86   template <typename T,
87             typename = std::enable_if_t<
88                 std::is_convertible_v<T, decltype(span(std::declval<T>()))>>>
Cmac(const T & key)89   explicit Cmac(const T& key) : Cmac(span(key)) {}
90 
91   /// Feeds `data` to the running AES-CMAC operation.
92   ///
93   /// The feeding can involve zero or more `Update()` calls and the order
94   /// matters.
95   ///
96   /// @note Any error during update will be reflected in the return value of
97   /// `Final()`.
98   ///
99   /// @param[in] data A byte string of any length to be fed to the running
100   /// AES-CMAC operation.
101   ///
102   /// @returns This same `Cmac` instance to allow chaining calls.
Update(ConstByteSpan data)103   Cmac& Update(ConstByteSpan data) {
104     if (data.empty()) {
105       return *this;
106     }
107 
108     if (state_ != State::kReady) {
109       PW_LOG_DEBUG("The backend is not ready/initialized");
110       return *this;
111     }
112 
113     if (!pw::crypto::aes::backend::DoUpdate(native_ctx_, data).ok()) {
114       PW_LOG_DEBUG("backend::DoUpdate() failed");
115       state_ = State::kError;
116       return *this;
117     }
118 
119     return *this;
120   }
121 
122   /// Finishes the AES-CMAC operations and outputs the final MAC.
123   ///
124   /// Additionally, `Final()` locks down the `Cmac` instance from any additional
125   /// use.
126   ///
127   /// @note Any error during initialization or update will be reflected in the
128   /// return value of `Final()`.
129   ///
130   /// @param[in] out_mac A byte span with a size of at least `kBlockSizeBytes`
131   /// where the final MAC will be written. If the span is larger than
132   /// `kBlockSizeBytes` only the first `kBlockSizeBytes` will be modified.
133   ///
134   /// @returns @pw_status{OK} if the AES-CMAC operation was successful and the
135   /// MAC was written to `out_mac`, @pw_status{RESOURCE_EXHAUSTED} if `out_mac`
136   /// is too small to write the MAC to, or @pw_status{INTERNAL} if an error was
137   /// encountered during the operation.
Final(aes::BlockSpan out_mac)138   Status Final(aes::BlockSpan out_mac) {
139     if (state_ != State::kReady) {
140       PW_LOG_DEBUG("The backend is not ready/initialized");
141       return Status::FailedPrecondition();
142     }
143 
144     auto status = pw::crypto::aes::backend::DoFinal(native_ctx_, out_mac);
145     if (!status.ok()) {
146       PW_LOG_DEBUG("backend::DoFinal() failed");
147       state_ = State::kError;
148       return status;
149     }
150 
151     state_ = State::kFinalized;
152     return OkStatus();
153   }
154 
155  private:
156   // Common state. Tracked by the front-end.
157   State state_;
158   // Backend-specific context.
159   pw::crypto::aes::backend::NativeCmacContext native_ctx_;
160 };
161 
162 }  // namespace pw::crypto::aes_cmac
163