1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include <memory>
8 #include "gtest/gtest.h"
9 #include "nss.h"
10 #include "nss_scoped_ptrs.h"
11 #include "pk11pub.h"
12 
13 namespace nss_test {
14 
15 class Pkcs11AESKeyWrapPadTest : public ::testing::Test {};
16 
17 // Encrypt an ephemeral EC key (U2F use case)
TEST_F(Pkcs11AESKeyWrapPadTest,WrapUnwrapECKey)18 TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapECKey) {
19   const uint32_t kwrappedBufLen = 256;
20   const uint32_t kPublicKeyLen = 65;
21   const uint32_t kOidLen = 65;
22   unsigned char param_buf[kOidLen];
23   unsigned char unwrap_buf[kPublicKeyLen];
24 
25   ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
26   ASSERT_NE(nullptr, slot);
27 
28   SECItem ecdsa_params = {siBuffer, param_buf, sizeof(param_buf)};
29   SECOidData* oid_data = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1);
30   ASSERT_NE(oid_data, nullptr);
31   ecdsa_params.data[0] = SEC_ASN1_OBJECT_ID;
32   ecdsa_params.data[1] = oid_data->oid.len;
33   memcpy(ecdsa_params.data + 2, oid_data->oid.data, oid_data->oid.len);
34   ecdsa_params.len = oid_data->oid.len + 2;
35 
36   SECKEYPublicKey* pub_tmp;
37   ScopedSECKEYPublicKey pub_key;
38   ScopedSECKEYPrivateKey priv_key(
39       PK11_GenerateKeyPair(slot.get(), CKM_EC_KEY_PAIR_GEN, &ecdsa_params,
40                            &pub_tmp, PR_FALSE, PR_TRUE, nullptr));
41   ASSERT_NE(nullptr, priv_key);
42   ASSERT_NE(nullptr, pub_tmp);
43   pub_key.reset(pub_tmp);
44 
45   // Generate a KEK.
46   ScopedPK11SymKey kek(
47       PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
48   ASSERT_NE(nullptr, kek);
49 
50   // Wrap the key
51   ScopedSECItem wrapped(::SECITEM_AllocItem(nullptr, nullptr, kwrappedBufLen));
52   ScopedSECItem param(PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP_PAD, nullptr));
53 
54   SECStatus rv = PK11_WrapPrivKey(slot.get(), kek.get(), priv_key.get(),
55                                   CKM_NSS_AES_KEY_WRAP_PAD, param.get(),
56                                   wrapped.get(), nullptr);
57   ASSERT_EQ(rv, SECSuccess);
58 
59   SECItem pubKey = {siBuffer, unwrap_buf, kPublicKeyLen};
60   CK_ATTRIBUTE_TYPE usages[] = {CKA_SIGN};
61   int usageCount = 1;
62 
63   ScopedSECKEYPrivateKey unwrapped(
64       PK11_UnwrapPrivKey(slot.get(), kek.get(), CKM_NSS_AES_KEY_WRAP_PAD,
65                          param.get(), wrapped.get(), nullptr, &pubKey, false,
66                          true, CKK_EC, usages, usageCount, nullptr));
67   ASSERT_EQ(0, PORT_GetError());
68   ASSERT_TRUE(!!unwrapped);
69 
70   // Try it with internal params allocation.
71   SECKEYPrivateKey* tmp = PK11_UnwrapPrivKey(
72       slot.get(), kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, nullptr, wrapped.get(),
73       nullptr, &pubKey, false, true, CKK_EC, usages, usageCount, nullptr);
74   ASSERT_EQ(0, PORT_GetError());
75   ASSERT_NE(nullptr, tmp);
76   unwrapped.reset(tmp);
77 }
78 
79 // Encrypt an ephemeral RSA key
TEST_F(Pkcs11AESKeyWrapPadTest,WrapUnwrapRsaKey)80 TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRsaKey) {
81   const uint32_t kwrappedBufLen = 648;
82   unsigned char unwrap_buf[kwrappedBufLen];
83 
84   ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
85   ASSERT_NE(nullptr, slot);
86 
87   PK11RSAGenParams rsa_param;
88   rsa_param.keySizeInBits = 1024;
89   rsa_param.pe = 65537L;
90 
91   SECKEYPublicKey* pub_tmp;
92   ScopedSECKEYPublicKey pub_key;
93   ScopedSECKEYPrivateKey priv_key(
94       PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN, &rsa_param,
95                            &pub_tmp, PR_FALSE, PR_FALSE, nullptr));
96   ASSERT_NE(nullptr, priv_key);
97   ASSERT_NE(nullptr, pub_tmp);
98   pub_key.reset(pub_tmp);
99 
100   // Generate a KEK.
101   ScopedPK11SymKey kek(
102       PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
103   ASSERT_NE(nullptr, kek);
104 
105   // Wrap the key
106   ScopedSECItem wrapped(::SECITEM_AllocItem(nullptr, nullptr, kwrappedBufLen));
107   ScopedSECItem param(PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP_PAD, nullptr));
108 
109   SECStatus rv = PK11_WrapPrivKey(slot.get(), kek.get(), priv_key.get(),
110                                   CKM_NSS_AES_KEY_WRAP_PAD, param.get(),
111                                   wrapped.get(), nullptr);
112   ASSERT_EQ(rv, SECSuccess);
113 
114   SECItem pubKey = {siBuffer, unwrap_buf, kwrappedBufLen};
115   CK_ATTRIBUTE_TYPE usages[] = {CKA_SIGN};
116   int usageCount = 1;
117 
118   ScopedSECKEYPrivateKey unwrapped(
119       PK11_UnwrapPrivKey(slot.get(), kek.get(), CKM_NSS_AES_KEY_WRAP_PAD,
120                          param.get(), wrapped.get(), nullptr, &pubKey, false,
121                          false, CKK_EC, usages, usageCount, nullptr));
122   ASSERT_EQ(0, PORT_GetError());
123   ASSERT_TRUE(!!unwrapped);
124 
125   ScopedSECItem priv_key_data(
126       PK11_ExportDERPrivateKeyInfo(priv_key.get(), nullptr));
127   ScopedSECItem unwrapped_data(
128       PK11_ExportDERPrivateKeyInfo(unwrapped.get(), nullptr));
129   EXPECT_TRUE(!!priv_key_data);
130   EXPECT_TRUE(!!unwrapped_data);
131   ASSERT_EQ(priv_key_data->len, unwrapped_data->len);
132   ASSERT_EQ(
133       0, memcmp(priv_key_data->data, unwrapped_data->data, priv_key_data->len));
134 }
135 
136 // Wrap a random that's a multiple of the block size, and compare the unwrap
137 // result.
TEST_F(Pkcs11AESKeyWrapPadTest,WrapUnwrapRandom_EvenBlock)138 TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_EvenBlock) {
139   const uint32_t kInputKeyLen = 128;
140   uint32_t out_len = 0;
141   std::vector<unsigned char> input_key(kInputKeyLen);
142   std::vector<unsigned char> wrapped_key(
143       kInputKeyLen + AES_BLOCK_SIZE);  // One block of padding
144   std::vector<unsigned char> unwrapped_key(
145       kInputKeyLen + AES_BLOCK_SIZE);  // One block of padding
146 
147   // Generate input key material
148   SECStatus rv = PK11_GenerateRandom(input_key.data(), input_key.size());
149   EXPECT_EQ(SECSuccess, rv);
150 
151   // Generate a KEK.
152   ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
153   ASSERT_NE(nullptr, slot);
154   ScopedPK11SymKey kek(
155       PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
156   ASSERT_NE(nullptr, kek);
157 
158   // Wrap the key
159   rv = PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
160                     wrapped_key.data(), &out_len,
161                     static_cast<unsigned int>(wrapped_key.size()),
162                     input_key.data(),
163                     static_cast<unsigned int>(input_key.size()));
164   ASSERT_EQ(SECSuccess, rv);
165 
166   rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
167                     unwrapped_key.data(), &out_len,
168                     static_cast<unsigned int>(unwrapped_key.size()),
169                     wrapped_key.data(), out_len);
170   ASSERT_EQ(SECSuccess, rv);
171   ASSERT_EQ(input_key.size(), out_len);
172   ASSERT_EQ(0, memcmp(input_key.data(), unwrapped_key.data(), out_len));
173 }
174 
175 // Wrap a random that's NOT a multiple of the block size, and compare the unwrap
176 // result.
TEST_F(Pkcs11AESKeyWrapPadTest,WrapUnwrapRandom_OddBlock1)177 TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_OddBlock1) {
178   const uint32_t kInputKeyLen = 65;
179   uint32_t out_len = 0;
180   std::vector<unsigned char> input_key(kInputKeyLen);
181   std::vector<unsigned char> wrapped_key(
182       kInputKeyLen + AES_BLOCK_SIZE);  // One block of padding
183   std::vector<unsigned char> unwrapped_key(
184       kInputKeyLen + AES_BLOCK_SIZE);  // One block of padding
185 
186   // Generate input key material
187   SECStatus rv = PK11_GenerateRandom(input_key.data(), input_key.size());
188   EXPECT_EQ(SECSuccess, rv);
189 
190   // Generate a KEK.
191   ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
192   ASSERT_NE(nullptr, slot);
193   ScopedPK11SymKey kek(
194       PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
195   ASSERT_NE(nullptr, kek);
196 
197   // Wrap the key
198   rv = PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
199                     wrapped_key.data(), &out_len,
200                     static_cast<unsigned int>(wrapped_key.size()),
201                     input_key.data(),
202                     static_cast<unsigned int>(input_key.size()));
203   ASSERT_EQ(SECSuccess, rv);
204 
205   rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
206                     unwrapped_key.data(), &out_len,
207                     static_cast<unsigned int>(unwrapped_key.size()),
208                     wrapped_key.data(), out_len);
209   ASSERT_EQ(SECSuccess, rv);
210   ASSERT_EQ(input_key.size(), out_len);
211   ASSERT_EQ(0, memcmp(input_key.data(), unwrapped_key.data(), out_len));
212 }
213 
214 // Wrap a random that's NOT a multiple of the block size, and compare the unwrap
215 // result.
TEST_F(Pkcs11AESKeyWrapPadTest,WrapUnwrapRandom_OddBlock2)216 TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_OddBlock2) {
217   const uint32_t kInputKeyLen = 63;
218   uint32_t out_len = 0;
219   std::vector<unsigned char> input_key(kInputKeyLen);
220   std::vector<unsigned char> wrapped_key(
221       kInputKeyLen + AES_BLOCK_SIZE);  // One block of padding
222   std::vector<unsigned char> unwrapped_key(
223       kInputKeyLen + AES_BLOCK_SIZE);  // One block of padding
224 
225   // Generate input key material
226   SECStatus rv = PK11_GenerateRandom(input_key.data(), input_key.size());
227   EXPECT_EQ(SECSuccess, rv);
228 
229   // Generate a KEK.
230   ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
231   ASSERT_NE(nullptr, slot);
232   ScopedPK11SymKey kek(
233       PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
234   ASSERT_NE(nullptr, kek);
235 
236   // Wrap the key
237   rv = PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
238                     wrapped_key.data(), &out_len, wrapped_key.size(),
239                     input_key.data(), input_key.size());
240   ASSERT_EQ(SECSuccess, rv);
241 
242   rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
243                     unwrapped_key.data(), &out_len,
244                     static_cast<unsigned int>(unwrapped_key.size()),
245                     wrapped_key.data(), out_len);
246   ASSERT_EQ(SECSuccess, rv);
247   ASSERT_EQ(input_key.size(), out_len);
248   ASSERT_EQ(0, memcmp(input_key.data(), unwrapped_key.data(), out_len));
249 }
250 
251 // Invalid long padding (over the block size, but otherwise valid)
TEST_F(Pkcs11AESKeyWrapPadTest,WrapUnwrapRandom_PaddingTooLong)252 TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_PaddingTooLong) {
253   const uint32_t kInputKeyLen = 32;
254   uint32_t out_len = 0;
255 
256   // Apply our own padding
257   const unsigned char buf[32] = {
258       0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
259       0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
260       0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20};
261   std::vector<unsigned char> wrapped_key(kInputKeyLen + AES_BLOCK_SIZE);
262   std::vector<unsigned char> unwrapped_key(kInputKeyLen + AES_BLOCK_SIZE);
263 
264   // Generate a KEK.
265   ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
266   ASSERT_NE(nullptr, slot);
267   ScopedPK11SymKey kek(
268       PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
269   ASSERT_NE(nullptr, kek);
270 
271   // Wrap the key
272   SECStatus rv =
273       PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP,  // Don't apply more padding
274                    /* param */ nullptr, wrapped_key.data(), &out_len,
275                    wrapped_key.size(), buf, sizeof(buf));
276   ASSERT_EQ(SECSuccess, rv);
277 
278   rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
279                     unwrapped_key.data(), &out_len,
280                     static_cast<unsigned int>(unwrapped_key.size()),
281                     wrapped_key.data(), out_len);
282   ASSERT_EQ(SECFailure, rv);
283 }
284 
285 // Invalid 0-length padding (there should be a full block if the message doesn't
286 // need to be padded)
TEST_F(Pkcs11AESKeyWrapPadTest,WrapUnwrapRandom_NoPadding)287 TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_NoPadding) {
288   const uint32_t kInputKeyLen = 32;
289   uint32_t out_len = 0;
290 
291   // Apply our own padding
292   const unsigned char buf[32] = {0};
293   std::vector<unsigned char> wrapped_key(kInputKeyLen + AES_BLOCK_SIZE);
294   std::vector<unsigned char> unwrapped_key(kInputKeyLen + AES_BLOCK_SIZE);
295 
296   // Generate a KEK.
297   ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
298   ASSERT_NE(nullptr, slot);
299   ScopedPK11SymKey kek(
300       PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
301   ASSERT_NE(nullptr, kek);
302 
303   // Wrap the key
304   SECStatus rv =
305       PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP,  // Don't apply more padding
306                    /* param */ nullptr, wrapped_key.data(), &out_len,
307                    wrapped_key.size(), buf, sizeof(buf));
308   ASSERT_EQ(SECSuccess, rv);
309 
310   rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
311                     unwrapped_key.data(), &out_len,
312                     static_cast<unsigned int>(unwrapped_key.size()),
313                     wrapped_key.data(), out_len);
314   ASSERT_EQ(SECFailure, rv);
315 }
316 
317 // Invalid padding
TEST_F(Pkcs11AESKeyWrapPadTest,WrapUnwrapRandom_BadPadding1)318 TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_BadPadding1) {
319   const uint32_t kInputKeyLen = 32;
320   uint32_t out_len = 0;
321 
322   // Apply our own padding
323   const unsigned char buf[32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
324                                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
325                                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
326                                  0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08,
327                                  0x08, 0x08, 0x08, 0x08};  // Check all 8 bytes
328   std::vector<unsigned char> wrapped_key(kInputKeyLen + AES_BLOCK_SIZE);
329   std::vector<unsigned char> unwrapped_key(kInputKeyLen + AES_BLOCK_SIZE);
330 
331   // Generate a KEK.
332   ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
333   ASSERT_NE(nullptr, slot);
334   ScopedPK11SymKey kek(
335       PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
336   ASSERT_NE(nullptr, kek);
337 
338   // Wrap the key
339   SECStatus rv =
340       PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP,  // Don't apply more padding
341                    /* param */ nullptr, wrapped_key.data(), &out_len,
342                    wrapped_key.size(), buf, sizeof(buf));
343   ASSERT_EQ(SECSuccess, rv);
344 
345   rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
346                     unwrapped_key.data(), &out_len,
347                     static_cast<unsigned int>(unwrapped_key.size()),
348                     wrapped_key.data(), out_len);
349   ASSERT_EQ(SECFailure, rv);
350 }
351 
352 // Invalid padding
TEST_F(Pkcs11AESKeyWrapPadTest,WrapUnwrapRandom_BadPadding2)353 TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_BadPadding2) {
354   const uint32_t kInputKeyLen = 32;
355   uint32_t out_len = 0;
356 
357   // Apply our own padding
358   const unsigned char
359       buf[32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
360                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
361                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
362                  0x00, 0x00, 0x00, 0x01, 0x02};  // Check first loop repeat
363   std::vector<unsigned char> wrapped_key(kInputKeyLen + AES_BLOCK_SIZE);
364   std::vector<unsigned char> unwrapped_key(kInputKeyLen + AES_BLOCK_SIZE);
365 
366   // Generate a KEK.
367   ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
368   ASSERT_NE(nullptr, slot);
369   ScopedPK11SymKey kek(
370       PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
371   ASSERT_NE(nullptr, kek);
372 
373   // Wrap the key
374   SECStatus rv =
375       PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP,  // Don't apply more padding
376                    /* param */ nullptr, wrapped_key.data(), &out_len,
377                    wrapped_key.size(), buf, sizeof(buf));
378   ASSERT_EQ(SECSuccess, rv);
379 
380   rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
381                     unwrapped_key.data(), &out_len,
382                     static_cast<unsigned int>(unwrapped_key.size()),
383                     wrapped_key.data(), out_len);
384   ASSERT_EQ(SECFailure, rv);
385 }
386 
387 // Minimum valid padding
TEST_F(Pkcs11AESKeyWrapPadTest,WrapUnwrapRandom_ShortValidPadding)388 TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_ShortValidPadding) {
389   const uint32_t kInputKeyLen = 32;
390   uint32_t out_len = 0;
391 
392   // Apply our own padding
393   const unsigned char buf[kInputKeyLen] = {
394       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
395       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
396       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};  // Minimum
397   std::vector<unsigned char> wrapped_key(kInputKeyLen + AES_BLOCK_SIZE);
398   std::vector<unsigned char> unwrapped_key(kInputKeyLen + AES_BLOCK_SIZE);
399 
400   // Generate a KEK.
401   ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
402   ASSERT_NE(nullptr, slot);
403   ScopedPK11SymKey kek(
404       PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
405   ASSERT_NE(nullptr, kek);
406 
407   // Wrap the key
408   SECStatus rv =
409       PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP,  // Don't apply more padding
410                    /* param */ nullptr, wrapped_key.data(), &out_len,
411                    wrapped_key.size(), buf, sizeof(buf));
412   ASSERT_EQ(SECSuccess, rv);
413 
414   rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
415                     unwrapped_key.data(), &out_len,
416                     static_cast<unsigned int>(unwrapped_key.size()),
417                     wrapped_key.data(), out_len);
418   ASSERT_EQ(SECSuccess, rv);
419   ASSERT_EQ(kInputKeyLen - 1, out_len);
420   ASSERT_EQ(0, memcmp(buf, unwrapped_key.data(), out_len));
421 }
422 
423 }  // namespace nss_test
424