Skip to content

Commit 9bde9e3

Browse files
turonjmeg-sfyemargolisandy31415
authored
[privacy] Add AES_CTR_encrypt/decrypt with tests. (#22108)
* [privacy] Add AES_CTR_encrypt/decrypt with tests. * [style] Remove magic words. * Apply suggestions from code review Co-authored-by: Jonathan Mégevand <[email protected]> * Update src/crypto/CHIPCryptoPAL.h Co-authored-by: Evgeny Margolis <[email protected]> * Use stdbuf to try to disable stdout buffering on darwin Co-authored-by: Jonathan Mégevand <[email protected]> Co-authored-by: Evgeny Margolis <[email protected]> Co-authored-by: Andrei Litvin <[email protected]>
1 parent 5f46524 commit 9bde9e3

File tree

5 files changed

+139
-3
lines changed

5 files changed

+139
-3
lines changed

scripts/tests/chiptest/runner.py

+5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import queue
1919
import re
2020
import subprocess
21+
import sys
2122
import threading
2223
import typing
2324

@@ -130,6 +131,10 @@ def RunSubprocess(self, cmd, name, wait=True, dependencies=[], timeout_seconds:
130131
logging.INFO, capture_delegate=self.capture_delegate,
131132
name=name + ' ERR')
132133

134+
if sys.platform == 'darwin':
135+
# Try harder to avoid any stdout buffering in our tests
136+
cmd = ['stdbuf', '-o0'] + cmd
137+
133138
if self.capture_delegate:
134139
self.capture_delegate.Log(name, 'EXECUTING %r' % cmd)
135140

src/credentials/GroupDataProviderImpl.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -1856,13 +1856,15 @@ CHIP_ERROR GroupDataProviderImpl::GroupKeyContext::MessageDecrypt(const ByteSpan
18561856
CHIP_ERROR GroupDataProviderImpl::GroupKeyContext::PrivacyEncrypt(const ByteSpan & input, const ByteSpan & nonce,
18571857
MutableByteSpan & output) const
18581858
{
1859-
return CHIP_ERROR_NOT_IMPLEMENTED;
1859+
return Crypto::AES_CTR_crypt(input.data(), input.size(), mPrivacyKey, Crypto::kAES_CCM128_Key_Length, nonce.data(),
1860+
nonce.size(), output.data());
18601861
}
18611862

18621863
CHIP_ERROR GroupDataProviderImpl::GroupKeyContext::PrivacyDecrypt(const ByteSpan & input, const ByteSpan & nonce,
18631864
MutableByteSpan & output) const
18641865
{
1865-
return CHIP_ERROR_NOT_IMPLEMENTED;
1866+
return Crypto::AES_CTR_crypt(input.data(), input.size(), mPrivacyKey, Crypto::kAES_CCM128_Key_Length, nonce.data(),
1867+
nonce.size(), output.data());
18661868
}
18671869

18681870
GroupDataProviderImpl::GroupSessionIterator * GroupDataProviderImpl::IterateGroupSessions(uint16_t session_id)

src/crypto/CHIPCryptoPAL.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,16 @@ CHIP_ERROR EcdsaAsn1SignatureToRaw(size_t fe_length_bytes, const ByteSpan & asn1
706706
return CHIP_NO_ERROR;
707707
}
708708

709+
CHIP_ERROR AES_CTR_crypt(const uint8_t * input, size_t input_length, const uint8_t * key, size_t key_length, const uint8_t * nonce,
710+
size_t nonce_length, uint8_t * output)
711+
{
712+
// Discard tag portion of CCM to apply only CTR mode encryption/decryption.
713+
constexpr size_t kTagLen = Crypto::kAES_CCM128_Tag_Length;
714+
uint8_t tag[kTagLen];
715+
716+
return AES_CCM_encrypt(input, input_length, nullptr, 0, key, key_length, nonce, nonce_length, output, tag, kTagLen);
717+
}
718+
709719
CHIP_ERROR GenerateCompressedFabricId(const Crypto::P256PublicKey & root_public_key, uint64_t fabric_id,
710720
MutableByteSpan & out_compressed_fabric_id)
711721
{

src/crypto/CHIPCryptoPAL.h

+22-1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ constexpr size_t kP256_PublicKey_Length = CHIP_CRYPTO_PUBLIC_KEY_SIZE_BYTES;
7878

7979
constexpr size_t kAES_CCM128_Key_Length = 128u / 8u;
8080
constexpr size_t kAES_CCM128_Block_Length = kAES_CCM128_Key_Length;
81+
constexpr size_t kAES_CCM128_Nonce_Length = 13;
82+
constexpr size_t kAES_CCM128_Tag_Length = 16;
8183

8284
/* These sizes are hardcoded here to remove header dependency on underlying crypto library
8385
* in a public interface file. The validity of these sizes is verified by static_assert in
@@ -614,11 +616,30 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c
614616
* @param plaintext Buffer to write plaintext into
615617
* @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
616618
**/
617-
618619
CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_length, const uint8_t * aad, size_t aad_length,
619620
const uint8_t * tag, size_t tag_length, const uint8_t * key, size_t key_length, const uint8_t * nonce,
620621
size_t nonce_length, uint8_t * plaintext);
621622

623+
/**
624+
* @brief A function that implements AES-CTR encryption/decryption
625+
*
626+
* This implements the AES-CTR-Encrypt/Decrypt() cryptographic primitives per sections
627+
* 3.7.1 and 3.7.2 of the specification. For an empty input, the user of the API
628+
* can provide an empty string, or a nullptr, and provide input as 0.
629+
* The output buffer can also be an empty string, or a nullptr for this case.
630+
*
631+
* @param input Input text to encrypt/decrypt
632+
* @param input_length Length of ciphertext
633+
* @param key Decryption key
634+
* @param key_length Length of Decryption key (in bytes)
635+
* @param nonce Encryption nonce
636+
* @param nonce_length Length of encryption nonce
637+
* @param output Buffer to write output into
638+
* @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
639+
**/
640+
CHIP_ERROR AES_CTR_crypt(const uint8_t * input, size_t input_length, const uint8_t * key, size_t key_length, const uint8_t * nonce,
641+
size_t nonce_length, uint8_t * output);
642+
622643
/**
623644
* @brief Generate a PKCS#10 CSR, usable for Matter, from a P256Keypair.
624645
*

src/crypto/tests/CHIPCryptoPALTest.cpp

+98
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,103 @@ static int test_entropy_source(void * data, uint8_t * output, size_t len, size_t
161161
return 0;
162162
}
163163

164+
struct AesCtrTestEntry
165+
{
166+
const uint8_t * key; ///< Key to use for AES-CTR-128 encryption/decryption -- 16 byte length
167+
const uint8_t * nonce; ///< Nonce to use for AES-CTR-128 encryption/decryption -- 13 byte length
168+
const uint8_t * plaintext;
169+
size_t plaintextLen;
170+
const uint8_t * ciphertext;
171+
size_t ciphertextLen;
172+
};
173+
174+
/**
175+
* Test vectors for AES-CTR-128 encryption/decryption.
176+
*
177+
* Sourced from: https://www.ietf.org/rfc/rfc3686.txt (Section 6)
178+
* Modified to use `IV = flags byte | 13 byte nonce | u16 counter` as defined in NIST SP 800-38A.
179+
*
180+
* All AES-CCM test vectors can be used as well, but those are already called to validate underlying AES-CCM functionality.
181+
*/
182+
const AesCtrTestEntry theAesCtrTestVector[] = {
183+
{
184+
.key = (const uint8_t *) "\xae\x68\x52\xf8\x12\x10\x67\xcc\x4b\xf7\xa5\x76\x55\x77\xf3\x9e",
185+
.nonce = (const uint8_t *) "\x00\x00\x00\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00",
186+
.plaintext = (const uint8_t *) "\x53\x69\x6e\x67\x6c\x65\x20\x62\x6c\x6f\x63\x6b\x20\x6d\x73\x67",
187+
.plaintextLen = 16,
188+
.ciphertext = (const uint8_t *) "\x0d\x0a\x6b\x6d\xc1\xf6\x9b\x4d\x14\xca\x4c\x15\x42\x22\x42\xc4",
189+
.ciphertextLen = 16,
190+
},
191+
{
192+
.key = (const uint8_t *) "\x7e\x24\x06\x78\x17\xfa\xe0\xd7\x43\xd6\xce\x1f\x32\x53\x91\x63",
193+
.nonce = (const uint8_t *) "\x00\x6c\xb6\xdb\xc0\x54\x3b\x59\xda\x48\xd9\x0b\x00",
194+
.plaintext = (const uint8_t *) "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
195+
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
196+
.plaintextLen = 32,
197+
.ciphertext = (const uint8_t *) "\x4f\x3d\xf9\x49\x15\x88\x4d\xe0\xdc\x0e\x30\x95\x0d\xe7\xa6\xe9"
198+
"\x5a\x91\x7e\x1d\x06\x42\x22\xdb\x2f\x6e\xc7\x3d\x99\x4a\xd9\x5f",
199+
.ciphertextLen = 32,
200+
}
201+
};
202+
203+
constexpr size_t kAesCtrTestVectorSize = sizeof(theAesCtrTestVector) / sizeof(theAesCtrTestVector[0]);
204+
205+
constexpr size_t KEY_LENGTH = Crypto::kAES_CCM128_Key_Length;
206+
constexpr size_t NONCE_LENGTH = Crypto::kAES_CCM128_Nonce_Length;
207+
208+
static void TestAES_CTR_128_Encrypt(nlTestSuite * inSuite, const AesCtrTestEntry * vector)
209+
{
210+
chip::Platform::ScopedMemoryBuffer<uint8_t> outBuffer;
211+
outBuffer.Alloc(vector->ciphertextLen);
212+
NL_TEST_ASSERT(inSuite, outBuffer);
213+
214+
CHIP_ERROR err = AES_CTR_crypt(vector->plaintext, vector->plaintextLen, vector->key, KEY_LENGTH, vector->nonce, NONCE_LENGTH,
215+
outBuffer.Get());
216+
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
217+
218+
bool outputMatches = memcmp(outBuffer.Get(), vector->ciphertext, vector->ciphertextLen) == 0;
219+
NL_TEST_ASSERT(inSuite, outputMatches);
220+
if (!outputMatches)
221+
{
222+
printf("\n Test failed due to mismatching ciphertext\n");
223+
}
224+
}
225+
226+
static void TestAES_CTR_128_Decrypt(nlTestSuite * inSuite, const AesCtrTestEntry * vector)
227+
{
228+
chip::Platform::ScopedMemoryBuffer<uint8_t> outBuffer;
229+
outBuffer.Alloc(vector->plaintextLen);
230+
NL_TEST_ASSERT(inSuite, outBuffer);
231+
232+
CHIP_ERROR err = AES_CTR_crypt(vector->ciphertext, vector->ciphertextLen, vector->key, KEY_LENGTH, vector->nonce, NONCE_LENGTH,
233+
outBuffer.Get());
234+
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
235+
236+
bool outputMatches = memcmp(outBuffer.Get(), vector->plaintext, vector->plaintextLen) == 0;
237+
NL_TEST_ASSERT(inSuite, outputMatches);
238+
if (!outputMatches)
239+
{
240+
printf("\n Test failed due to mismatching plaintext\n");
241+
}
242+
}
243+
244+
static void TestAES_CTR_128CryptTestVectors(nlTestSuite * inSuite, void * inContext)
245+
{
246+
HeapChecker heapChecker(inSuite);
247+
int numOfTestsRan = 0;
248+
for (size_t vectorIndex = 0; vectorIndex < kAesCtrTestVectorSize; vectorIndex++)
249+
{
250+
const AesCtrTestEntry * vector = &theAesCtrTestVector[vectorIndex];
251+
if (vector->plaintextLen > 0)
252+
{
253+
numOfTestsRan++;
254+
TestAES_CTR_128_Encrypt(inSuite, vector);
255+
TestAES_CTR_128_Decrypt(inSuite, vector);
256+
}
257+
}
258+
NL_TEST_ASSERT(inSuite, numOfTestsRan > 0);
259+
}
260+
164261
static void TestAES_CCM_128EncryptTestVectors(nlTestSuite * inSuite, void * inContext)
165262
{
166263
HeapChecker heapChecker(inSuite);
@@ -2279,6 +2376,7 @@ static const nlTest sTests[] = {
22792376
NL_TEST_DEF("Test decrypting AES-CCM-128 invalid key", TestAES_CCM_128DecryptInvalidKey),
22802377
NL_TEST_DEF("Test decrypting AES-CCM-128 invalid nonce", TestAES_CCM_128DecryptInvalidNonceLen),
22812378
NL_TEST_DEF("Test decrypting AES-CCM-128 Containers", TestAES_CCM_128Containers),
2379+
NL_TEST_DEF("Test encrypt/decrypt AES-CTR-128 test vectors", TestAES_CTR_128CryptTestVectors),
22822380
NL_TEST_DEF("Test ASN.1 signature conversion routines", TestAsn1Conversions),
22832381
NL_TEST_DEF("Test Integer to ASN.1 DER conversion", TestRawIntegerToDerValidCases),
22842382
NL_TEST_DEF("Test Integer to ASN.1 DER conversion error cases", TestRawIntegerToDerInvalidCases),

0 commit comments

Comments
 (0)