F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
AesGcmDecryptor.cpp
Go to the documentation of this file.
1 // ======================================================================
2 // \title AesGcmDecryptor.cpp
3 // \author cadena and claradavisb
4 // \brief cpp file for AesGcmDecryptor component implementation class
5 // ======================================================================
6 
8 #include <openssl/crypto.h>
9 #include <openssl/evp.h>
11 
12 namespace Svc {
13 
14 namespace Ccsds {
15 
16 // ----------------------------------------------------------------------
17 // Component construction and destruction
18 // ----------------------------------------------------------------------
19 
21 static constexpr U32 GCM_IV_LEN = 12;
23 static constexpr U32 GCM_TAG_LEN = 16;
25 static constexpr FwSizeType AES_256_KEY_LEN = 32;
26 
27 // Build the cipher state once so that decrypting a frame allocates nothing.
28 // Only the key and the IV change and those are supplied per
29 // frame by a single EVP_DecryptInit_ex.
30 AesGcmDecryptor ::AesGcmDecryptor(const char* const compName)
31  : AesGcmDecryptorComponentBase(compName),
32  m_cipher(nullptr),
33  m_ctx(nullptr),
34  m_aad(0, 0),
35  m_aadVcId(0),
36  m_aadSaIndex(0) {
37  this->m_cipher = EVP_CIPHER_fetch(nullptr, "AES-256-GCM", nullptr);
38  FW_ASSERT(this->m_cipher != nullptr);
39  this->m_ctx = EVP_CIPHER_CTX_new();
40  FW_ASSERT(this->m_ctx != nullptr);
41  int status = EVP_DecryptInit_ex(this->m_ctx, this->m_cipher, nullptr, nullptr, nullptr);
42  FW_ASSERT(status == 1, static_cast<FwAssertArgType>(status));
43  status = EVP_CIPHER_CTX_ctrl(this->m_ctx, EVP_CTRL_GCM_SET_IVLEN, static_cast<int>(GCM_IV_LEN), nullptr);
44  FW_ASSERT(status == 1, static_cast<FwAssertArgType>(status));
45 }
46 
48  EVP_CIPHER_CTX_free(this->m_ctx);
49  EVP_CIPHER_free(this->m_cipher);
50 }
51 
52 // ----------------------------------------------------------------------
53 // Handler implementations for typed input ports
54 // ----------------------------------------------------------------------
55 
56 void AesGcmDecryptor ::decryptIn_handler(FwIndexType portNum,
57  U16 securityAssociationIndex,
58  Fw::Buffer& data,
59  const ComCfg::FrameContext& context) {
60  FW_ASSERT(this->m_ctx != nullptr);
61 
62  // Layout after CcsdsSdlsDeframer strips the SPI: IV (12) | ciphertext (N) | MAC (16).
63 
64  if (data.getSize() < GCM_IV_LEN + GCM_TAG_LEN) {
65  // Too short to hold an IV and a MAC
66  this->decryptOut_out(0, Svc::Ccsds::SdlsStatus::DECRYPTION_FAILURE, data, context);
67  return;
68  }
69 
71  const Svc::Ccsds::SdlsStatus keyStatus = this->keyGet_out(0, securityAssociationIndex, key);
72  if ((keyStatus != Svc::Ccsds::SdlsStatus::SUCCESS) || (key.getSize() != AES_256_KEY_LEN)) {
73  // A wrong-sized key would decrypt under the wrong material rather than failing
74  this->decryptOut_out(0, Svc::Ccsds::SdlsStatus::KEY_ERROR, data, context);
75  return;
76  }
77 
78  U8* const iv = data.getData();
79  U8* const ciphertext = data.getData() + GCM_IV_LEN;
80  const U32 cipherLen = static_cast<U32>(data.getSize()) - GCM_IV_LEN - GCM_TAG_LEN;
81  U8* const tag = ciphertext + cipherLen;
82 
83  // Authenticated but not encrypted; the VC travels in the context
84  // The mask depends only on the VC and the SA, so it is rebuilt when either changes
85  // rather than per frame
86  const U8 vcId = context.get_vcId();
87  if ((vcId != this->m_aadVcId) || (securityAssociationIndex != this->m_aadSaIndex)) {
88  this->m_aad = Svc::Ccsds::Utils::SdlsTcAuthMask(vcId, securityAssociationIndex);
89  this->m_aadVcId = vcId;
90  this->m_aadSaIndex = securityAssociationIndex;
91  }
92 
93  int len = 0;
94  int plainLen = 0;
95 
96  const bool rekeyed = EVP_DecryptInit_ex(this->m_ctx, nullptr, nullptr, key.getBuffAddr(), iv) == 1;
97  // The cipher context holds the key schedule now, so the stack copy is dead.
98  // OPENSSL_cleanse wipes it so the key cannot be recovered from a memory dump.
99  OPENSSL_cleanse(key.getBuffAddr(), key.getCapacity());
100  const bool aadAbsorbed = rekeyed && (EVP_DecryptUpdate(this->m_ctx, nullptr, &len, this->m_aad.bytes,
101  static_cast<int>(sizeof(this->m_aad.bytes))) == 1);
102  const bool decrypted =
103  aadAbsorbed && (EVP_DecryptUpdate(this->m_ctx, ciphertext, &len, ciphertext, static_cast<int>(cipherLen)) == 1);
104  const bool tagSet =
105  decrypted && (EVP_CIPHER_CTX_ctrl(this->m_ctx, EVP_CTRL_GCM_SET_TAG, static_cast<int>(GCM_TAG_LEN), tag) == 1);
106  if (!tagSet) {
107  this->decryptOut_out(0, Svc::Ccsds::SdlsStatus::DECRYPTION_FAILURE, data, context);
108  return;
109  }
110  plainLen = len;
111 
112  // Verify the MAC
113  if (EVP_DecryptFinal_ex(this->m_ctx, ciphertext + plainLen, &len) != 1) {
114  this->decryptOut_out(0, Svc::Ccsds::SdlsStatus::MAC_VERIFICATION_FAILURE, data, context);
115  return;
116  }
117  FW_ASSERT(len == 0, static_cast<FwAssertArgType>(len));
118 
119  // Move to the plaintext
120  data.advance(static_cast<FwSignedSizeType>(GCM_IV_LEN));
121  data.setSize(static_cast<Fw::Buffer::SizeType>(plainLen));
122  this->decryptOut_out(0, Svc::Ccsds::SdlsStatus::SUCCESS, data, context);
123 }
124 
125 void AesGcmDecryptor ::decryptReturnIn_handler(FwIndexType portNum,
126  Fw::Buffer& data,
127  const ComCfg::FrameContext& context) {
128  this->bufferReturnOut_out(0, data, context);
129 }
130 
131 } // namespace Ccsds
132 
133 } // namespace Svc
PlatformSizeType FwSizeType
void setSize(FwSizeType size)
Definition: Buffer.cpp:131
Serializable::SizeType getSize() const override
Get current buffer size.
U8 * getData() const
Definition: Buffer.cpp:82
void advance(FwSignedSizeType amount)
Definition: Buffer.cpp:106
static constexpr U32 GCM_IV_LEN
Length of the AES-GCM initialization vector, in bytes.
Serializable::SizeType getCapacity() const override
Get buffer capacity.
Status of an SDLS (Space Data Link Security) encryption/decryption request.
static constexpr FwSizeType AES_256_KEY_LEN
Length of an AES-256 key, in bytes.
U8 * getBuffAddr()
Get buffer address for data filling (non-const version)
U8 get_vcId() const
Get member vcId.
AesGcmDecryptor(const char *const compName)
Construct AesGcmDecryptor object.
~AesGcmDecryptor()
Destroy AesGcmDecryptor object.
Frame failed its authentication check.
static constexpr U32 GCM_TAG_LEN
Length of the AES-GCM authentication tag (the SDLS MAC), in bytes.
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.h:54
FwSizeType getSize() const
Definition: Buffer.cpp:90
PlatformIndexType FwIndexType
Type used to pass context info between components during framing/deframing.
RateGroupDivider component implementation.
Request completed successfully.
#define FW_ASSERT(...)
Definition: Assert.hpp:14