Software APIs
rsa_2048_encryption_functest.c
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
6 #include "sw/device/lib/crypto/drivers/entropy.h"
7 #include "sw/device/lib/crypto/impl/integrity.h"
11 #include "sw/device/lib/testing/profile.h"
12 #include "sw/device/lib/testing/test_framework/check.h"
14 
15 // Module for status messages.
16 #define MODULE_ID MAKE_MODULE_ID('t', 's', 't')
17 
18 enum {
19  kRsa2048NumBytes = 2048 / 8,
20  kRsa2048NumWords = kRsa2048NumBytes / sizeof(uint32_t),
21 };
22 
23 // Note: The private key and valid signatures for this test were generated
24 // out-of-band using the PyCryptodome Python library.
25 
26 // Test RSA-2048 key pair.
27 static uint32_t kTestModulus[kRsa2048NumWords] = {
28  0x40d984b1, 0x3611356d, 0x9eb2f35c, 0x031a892c, 0x16354662, 0x6a260bad,
29  0xb2b807d6, 0xb7de7ccb, 0x278492e0, 0x41adab06, 0x9e60110f, 0x1414eeff,
30  0x8b80e14e, 0x5eb5ae79, 0x0d98fa5b, 0x58bece1f, 0xcf6bdca8, 0x82f5611f,
31  0x351e3869, 0x075005d6, 0xe813fe23, 0xdd967a37, 0x682d1c41, 0x9fdd2d8c,
32  0x21bdd5fc, 0x4fc459c7, 0x508c9293, 0x1f9ac759, 0x55aacb04, 0x58389f05,
33  0x0d0b00fb, 0x59bb4141, 0x68f9e0bf, 0xc2f1a546, 0x0a71ad19, 0x9c400301,
34  0xa4f8ecb9, 0xcdf39538, 0xaabe9cb0, 0xd9f7b2dc, 0x0e8b292d, 0x8ef6c717,
35  0x720e9520, 0xb0c6a23e, 0xda1e92b1, 0x8b6b4800, 0x2f25082b, 0x7f2d6711,
36  0x426fc94f, 0x9926ba5a, 0x89bd4d2b, 0x977718d5, 0x5a8406be, 0x87d090f3,
37  0x639f9975, 0x5948488b, 0x1d3d9cd7, 0x28c7956b, 0xebb97a3e, 0x1edbf4e2,
38  0x105cc797, 0x924ec514, 0x146810df, 0xb1ab4a49,
39 };
40 static uint32_t kTestPrivateExponent[kRsa2048NumWords] = {
41  0x0b19915b, 0xa6a935e6, 0x426b2e10, 0xb4ff0629, 0x7322343b, 0x3f28c8d5,
42  0x190757ce, 0x87409d6b, 0xd88e282b, 0x01c13c2a, 0xebb79189, 0x74cbeab9,
43  0x93de5d54, 0xae1bc80a, 0x083a75f2, 0xd574d229, 0xeb46696e, 0x7648cfb6,
44  0xe7ad1b36, 0xbd0e81b2, 0x19c72703, 0xebea5085, 0xf8c7d152, 0x34dcf84d,
45  0xa437187f, 0x41e4f88e, 0xe4e35f9f, 0xcd8bc6f8, 0x7f98e2f2, 0xffdf75ca,
46  0x3698226e, 0x903f2a56, 0xbf21a6dc, 0x97cbf653, 0xe9d80cb3, 0x55dc1685,
47  0xe0ebae21, 0xc8171e18, 0x8e73d26d, 0xbbdbaac1, 0x886e8007, 0x673c9da4,
48  0xe2cb0698, 0xa9f1ba2d, 0xedab4f0a, 0x197e890c, 0x65e7e736, 0x1de28f24,
49  0x57cf5137, 0x631ff441, 0x22539942, 0xcee3fd41, 0xd22b5f8a, 0x995dd87a,
50  0xcaa6815c, 0x08ca0fd3, 0x8f996093, 0x30b7c446, 0xf69b11f7, 0xa298dd00,
51  0xfd4e8120, 0x059df602, 0x25feb268, 0x0f3f749e,
52 };
53 static uint32_t kTestPublicExponent = 65537;
54 
55 // Message data for testing.
56 static const unsigned char kTestMessage[] = "Test message.";
57 static const size_t kTestMessageLen = sizeof(kTestMessage) - 1;
58 
59 // OAEP label for testing.
60 static const unsigned char kTestLabel[] = "Test label.";
61 static const size_t kTestLabelLen = sizeof(kTestLabel) - 1;
62 
63 // Hash mode for testing (and digest length in bytes).
64 static const otcrypto_hash_mode_t kTestHashMode = kOtcryptoHashModeSha256;
65 static const size_t kTestHashModeDigestBytes = 256 / 8;
66 
67 // Maximum plaintext length for OAEP (see IETF RFC 8017).
68 static const size_t kMaxPlaintextBytes =
69  kRsa2048NumBytes - 2 * kTestHashModeDigestBytes - 2;
70 
71 // Valid ciphertext for `kTestMessage` with the test private key, using OAEP
72 // encoding with `kTestLabel` and `kTestHashMode`.
73 static const uint32_t kValidCiphertext[kRsa2048NumWords] = {
74  0xfd4efa2a, 0x98502230, 0x8f40a23d, 0xf1bc68ec, 0x32c09a86, 0x31a34a7f,
75  0x4cc36d4d, 0xebde83bb, 0xd8641f7e, 0xedc26ed4, 0x8cd83ce6, 0xca3e0696,
76  0x5a425138, 0xd5d55a43, 0x4666b6eb, 0x7d031dee, 0xbc92a18d, 0xce7f14be,
77  0x768d170, 0xa3b26259, 0x668cf732, 0x72b44d0e, 0xd9f35df1, 0x67e194af,
78  0xf4a47c8a, 0x8c0be5ee, 0x3b132be9, 0x797cdeb, 0x5ac41ab2, 0x960bd1bb,
79  0x4d5f9c16, 0x1b40df52, 0x1cc85cae, 0x897f104f, 0xa6d56f86, 0x13d59592,
80  0x741b5a79, 0x15732dbb, 0xa792b600, 0x8a1a6ad8, 0x6192b34b, 0xd5516b1a,
81  0xab6c8133, 0x4b820cb3, 0xdec5f9b5, 0x9d479d3a, 0xd8e8109c, 0xe9e79346,
82  0x91e4c925, 0x730c258, 0x3ae71747, 0x50ab1e5e, 0x931bd40a, 0x351d2440,
83  0xb5e9273d, 0xd07a5e7b, 0x84487ef2, 0xfa2c3eae, 0x60a289db, 0x533d9a42,
84  0x3473ae8, 0x6b43b4a4, 0x4944f45f, 0x9588b044,
85 };
86 
87 /**
88  * Helper function to run the RSA-2048 encryption routine.
89  *
90  * Packages input into cryptolib-style structs and calls `otcrypto_rsa_encrypt`
91  * using the constant test private key.
92  *
93  * @param msg Message to encrypt.
94  * @param msg_len Message length in bytes.
95  * @param label Label for OAEP padding.
96  * @param label_len Label length in bytes.
97  * @param[out] ciphertext Buffer for the generated ciphertext (2048 bits).
98  * @return OK or error.
99  */
100 static status_t run_rsa_2048_encrypt(const uint8_t *msg, size_t msg_len,
101  const uint8_t *label, size_t label_len,
102  uint32_t *ciphertext) {
103  // Construct the public key.
104  otcrypto_const_word32_buf_t modulus = {
105  .data = kTestModulus,
106  .len = ARRAYSIZE(kTestModulus),
107  };
108  uint32_t public_key_data[ceil_div(kOtcryptoRsa2048PublicKeyBytes,
109  sizeof(uint32_t))];
110  otcrypto_unblinded_key_t public_key = {
111  .key_mode = kOtcryptoKeyModeRsaEncryptOaep,
112  .key_length = kOtcryptoRsa2048PublicKeyBytes,
113  .key = public_key_data,
114  };
115  TRY(otcrypto_rsa_public_key_construct(kOtcryptoRsaSize2048, modulus,
116  kTestPublicExponent, &public_key));
117 
118  otcrypto_const_byte_buf_t msg_buf = {.data = msg, .len = msg_len};
119  otcrypto_const_byte_buf_t label_buf = {.data = label, .len = label_len};
120  otcrypto_word32_buf_t ciphertext_buf = {
121  .data = ciphertext,
122  .len = kRsa2048NumWords,
123  };
124  uint64_t t_start = profile_start();
125  TRY(otcrypto_rsa_encrypt(&public_key, kTestHashMode, msg_buf, label_buf,
126  ciphertext_buf));
127  profile_end_and_print(t_start, "RSA-2048 encryption");
128 
129  return OK_STATUS();
130 }
131 
132 /**
133  * Helper function to run the RSA-2048 decryption routine.
134  *
135  * Packages input into cryptolib-style structs and calls `otcrypto_rsa_decrypt`
136  * using the constant test private key.
137  *
138  * The caller must allocate enough space at `msg` for the maximum message
139  * length, `kMaxPlaintextBytes`. The actual message length is returned in
140  * `msg_len`.
141  *
142  * @param label Label for OAEP padding.
143  * @param label_len Label length in bytes.
144  * @param ciphertext Ciphertext to decrypt (2048 bits).
145  * @param[out] msg Decrypted message.
146  * @param[out] msg_len Message length in bytes.
147  * @return OK or error.
148  */
149 static status_t run_rsa_2048_decrypt(const uint8_t *label, size_t label_len,
150  const uint32_t *ciphertext, uint8_t *msg,
151  size_t *msg_len) {
152  // Create two shares for the private exponent (second share is all-zero).
153  otcrypto_const_word32_buf_t d_share0 = {
154  .data = kTestPrivateExponent,
155  .len = ARRAYSIZE(kTestPrivateExponent),
156  };
157  uint32_t share1[ARRAYSIZE(kTestPrivateExponent)] = {0};
158  otcrypto_const_word32_buf_t d_share1 = {
159  .data = share1,
160  .len = ARRAYSIZE(share1),
161  };
162 
163  // Construct the private key.
164  otcrypto_key_config_t private_key_config = {
165  .version = kOtcryptoLibVersion1,
166  .key_mode = kOtcryptoKeyModeRsaEncryptOaep,
167  .key_length = kOtcryptoRsa2048PrivateKeyBytes,
168  .hw_backed = kHardenedBoolFalse,
169  .security_level = kOtcryptoKeySecurityLevelLow,
170  };
171  size_t keyblob_words =
173  uint32_t keyblob[keyblob_words];
174  otcrypto_blinded_key_t private_key = {
175  .config = private_key_config,
176  .keyblob = keyblob,
177  .keyblob_length = kOtcryptoRsa2048PrivateKeyblobBytes,
178  };
179  otcrypto_const_word32_buf_t modulus = {
180  .data = kTestModulus,
181  .len = ARRAYSIZE(kTestModulus),
182  };
183  TRY(otcrypto_rsa_private_key_from_exponents(kOtcryptoRsaSize2048, modulus,
184  kTestPublicExponent, d_share0,
185  d_share1, &private_key));
186 
187  otcrypto_byte_buf_t plaintext_buf = {.data = msg, .len = kMaxPlaintextBytes};
188  otcrypto_const_byte_buf_t label_buf = {.data = label, .len = label_len};
189  otcrypto_const_word32_buf_t ciphertext_buf = {
190  .data = ciphertext,
191  .len = kRsa2048NumWords,
192  };
193  uint64_t t_start = profile_start();
194  TRY(otcrypto_rsa_decrypt(&private_key, kTestHashMode, ciphertext_buf,
195  label_buf, plaintext_buf, msg_len));
196  profile_end_and_print(t_start, "RSA-2048 decryption");
197 
198  return OK_STATUS();
199 }
200 
201 status_t oaep_decrypt_valid_test(void) {
202  // Decrypt the valid ciphertext.
203  uint8_t actual_msg[kMaxPlaintextBytes];
204  size_t actual_msg_len;
205  TRY(run_rsa_2048_decrypt(kTestLabel, kTestLabelLen, kValidCiphertext,
206  actual_msg, &actual_msg_len));
207 
208  // Compare to the expected plaintext.
209  TRY_CHECK(actual_msg_len == kTestMessageLen);
210  TRY_CHECK_ARRAYS_EQ(actual_msg, kTestMessage, actual_msg_len);
211  return OK_STATUS();
212 }
213 
214 status_t oaep_encrypt_decrypt_test(void) {
215  // Encrypt the message. Because OAEP is not deterministic, we cannot compare
216  // to an exact expected value, but we can check that it successfully
217  // decrypts.
218  uint32_t ciphertext[kRsa2048NumWords];
219  TRY(run_rsa_2048_encrypt(kTestMessage, kTestMessageLen, kTestLabel,
220  kTestLabelLen, ciphertext));
221 
222  // Decrypt the message.
223  uint8_t actual_msg[kMaxPlaintextBytes];
224  size_t actual_msg_len;
225  TRY(run_rsa_2048_decrypt(kTestLabel, kTestLabelLen, ciphertext, actual_msg,
226  &actual_msg_len));
227 
228  // Compare to the original message.
229  TRY_CHECK(actual_msg_len == kTestMessageLen);
230  TRY_CHECK_ARRAYS_EQ(actual_msg, kTestMessage, actual_msg_len);
231  return OK_STATUS();
232 }
233 
234 OTTF_DEFINE_TEST_CONFIG();
235 
236 bool test_main(void) {
237  status_t test_result = OK_STATUS();
238  CHECK_STATUS_OK(entropy_complex_init());
239  EXECUTE_TEST(test_result, oaep_decrypt_valid_test);
240  EXECUTE_TEST(test_result, oaep_encrypt_decrypt_test);
241  return status_ok(test_result);
242 }