1 #include <immintrin.h> 2 #include <stddef.h> 3 #include <stdint.h> 4 #include <string.h> 5 6 #include "address.h" 7 #include "api.h" 8 #include "fors.h" 9 #include "hash.h" 10 #include "hash_state.h" 11 #include "params.h" 12 #include "randombytes.h" 13 #include "thash.h" 14 #include "utils.h" 15 #include "wots.h" 16 17 18 /** 19 * Computes the leaf at a given address. First generates the WOTS key pair, 20 * then computes leaf by hashing horizontally. 21 */ 22 static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, 23 const unsigned char *pub_seed, 24 uint32_t addr_idx, const uint32_t tree_addr[8], 25 const hash_state *hash_state_seeded) { 26 unsigned char pk[PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_WOTS_BYTES]; 27 uint32_t wots_addr[8] = {0}; 28 uint32_t wots_pk_addr[8] = {0}; 29 30 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_set_type( 31 wots_addr, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_ADDR_TYPE_WOTS); 32 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_set_type( 33 wots_pk_addr, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_ADDR_TYPE_WOTSPK); 34 35 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_copy_subtree_addr( 36 wots_addr, tree_addr); 37 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_set_keypair_addr( 38 wots_addr, addr_idx); 39 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_wots_gen_pk( 40 pk, sk_seed, pub_seed, wots_addr, hash_state_seeded); 41 42 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_copy_keypair_addr( 43 wots_pk_addr, wots_addr); 44 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_thash_WOTS_LEN( 45 leaf, pk, pub_seed, wots_pk_addr, hash_state_seeded); 46 } 47 48 /* 49 * Returns the length of a secret key, in bytes 50 */ 51 size_t PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_crypto_sign_secretkeybytes(void) { 52 return PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_CRYPTO_SECRETKEYBYTES; 53 } 54 55 /* 56 * Returns the length of a public key, in bytes 57 */ 58 size_t PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_crypto_sign_publickeybytes(void) { 59 return PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_CRYPTO_PUBLICKEYBYTES; 60 } 61 62 /* 63 * Returns the length of a signature, in bytes 64 */ 65 size_t PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_crypto_sign_bytes(void) { 66 return PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_CRYPTO_BYTES; 67 } 68 69 /* 70 * Returns the length of the seed required to generate a key pair, in bytes 71 */ 72 size_t PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_crypto_sign_seedbytes(void) { 73 return PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_CRYPTO_SEEDBYTES; 74 } 75 76 /* 77 * Generates an SPX key pair given a seed of length 78 * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] 79 * Format pk: [PUB_SEED || root] 80 */ 81 int PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_crypto_sign_seed_keypair( 82 uint8_t *pk, uint8_t *sk, const uint8_t *seed) { 83 /* We do not need the auth path in key generation, but it simplifies the 84 code to have just one treehash routine that computes both root and path 85 in one function. */ 86 unsigned char auth_path[PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_TREE_HEIGHT * PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N]; 87 uint32_t top_tree_addr[8] = {0}; 88 hash_state hash_state_seeded; 89 90 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_set_layer_addr( 91 top_tree_addr, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_D - 1); 92 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_set_type( 93 top_tree_addr, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_ADDR_TYPE_HASHTREE); 94 95 /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ 96 memcpy(sk, seed, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_CRYPTO_SEEDBYTES); 97 98 memcpy(pk, sk + 2 * PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N); 99 100 /* This hook allows the hash function instantiation to do whatever 101 preparation or computation it needs, based on the public seed. */ 102 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_initialize_hash_function(&hash_state_seeded, pk, sk); 103 104 /* Compute root node of the top-most subtree. */ 105 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_treehash_TREE_HEIGHT( 106 sk + 3 * PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N, auth_path, sk, sk + 2 * PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N, 0, 0, 107 wots_gen_leaf, top_tree_addr, &hash_state_seeded); 108 109 memcpy(pk + PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N, sk + 3 * PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N); 110 111 return 0; 112 } 113 114 /* 115 * Generates an SPX key pair. 116 * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] 117 * Format pk: [PUB_SEED || root] 118 */ 119 int PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_crypto_sign_keypair( 120 uint8_t *pk, uint8_t *sk) { 121 122 // guarantee alignment of pk 123 union { 124 __m128 _x[PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_CRYPTO_PUBLICKEYBYTES / 16]; 125 uint8_t pk[PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_CRYPTO_PUBLICKEYBYTES]; 126 } aligned_pk; 127 128 // guarantee alignment of sk 129 union { 130 __m128 _x[PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_CRYPTO_SECRETKEYBYTES / 16]; 131 uint8_t sk[PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_CRYPTO_SECRETKEYBYTES]; 132 } aligned_sk; 133 134 union { 135 __m128 _x[PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_CRYPTO_SEEDBYTES / 16]; 136 uint8_t seed[PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_CRYPTO_SEEDBYTES]; 137 } aligned_seed; 138 randombytes(aligned_seed.seed, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_CRYPTO_SEEDBYTES); 139 140 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_crypto_sign_seed_keypair( 141 aligned_pk.pk, aligned_sk.sk, aligned_seed.seed); 142 memcpy(pk, aligned_pk.pk, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_CRYPTO_PUBLICKEYBYTES); 143 memcpy(sk, aligned_sk.sk, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_CRYPTO_SECRETKEYBYTES); 144 145 return 0; 146 } 147 148 /** 149 * Returns an array containing a detached signature. 150 */ 151 int PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_crypto_sign_signature( 152 uint8_t *sig, size_t *siglen, 153 const uint8_t *m, size_t mlen, const uint8_t *sk) { 154 // guarantee alignment of sk 155 union { 156 __m128 *_x; 157 uint8_t sk[PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_CRYPTO_SECRETKEYBYTES]; 158 } aligned_sk; 159 memcpy(aligned_sk.sk, sk, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_CRYPTO_SECRETKEYBYTES); 160 sk = aligned_sk.sk; 161 162 // guarantee alignment of sig 163 union { 164 __m128 *_x; 165 uint8_t sig[PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_BYTES]; 166 } aligned_sig; 167 uint8_t *orig_sig = sig; 168 sig = (uint8_t *)aligned_sig.sig; 169 170 const unsigned char *sk_seed = sk; 171 const unsigned char *sk_prf = sk + PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N; 172 const unsigned char *pk = sk + 2 * PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N; 173 const unsigned char *pub_seed = pk; 174 175 unsigned char optrand[PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N]; 176 unsigned char mhash[PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_FORS_MSG_BYTES]; 177 unsigned char root[PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N]; 178 uint32_t i; 179 uint64_t tree; 180 uint32_t idx_leaf; 181 uint32_t wots_addr[8] = {0}; 182 uint32_t tree_addr[8] = {0}; 183 184 hash_state hash_state_seeded; 185 186 /* This hook allows the hash function instantiation to do whatever 187 preparation or computation it needs, based on the public seed. */ 188 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_initialize_hash_function( 189 &hash_state_seeded, 190 pub_seed, sk_seed); 191 192 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_set_type( 193 wots_addr, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_ADDR_TYPE_WOTS); 194 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_set_type( 195 tree_addr, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_ADDR_TYPE_HASHTREE); 196 197 /* Optionally, signing can be made non-deterministic using optrand. 198 This can help counter side-channel attacks that would benefit from 199 getting a large number of traces when the signer uses the same nodes. */ 200 randombytes(optrand, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N); 201 /* Compute the digest randomization value. */ 202 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_gen_message_random( 203 sig, sk_prf, optrand, m, mlen, &hash_state_seeded); 204 205 /* Derive the message digest and leaf index from R, PK and M. */ 206 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_hash_message( 207 mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); 208 sig += PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N; 209 210 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_set_tree_addr(wots_addr, tree); 211 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_set_keypair_addr( 212 wots_addr, idx_leaf); 213 214 /* Sign the message hash using FORS. */ 215 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_fors_sign( 216 sig, root, mhash, sk_seed, pub_seed, wots_addr, &hash_state_seeded); 217 sig += PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_FORS_BYTES; 218 219 for (i = 0; i < PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_D; i++) { 220 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_set_layer_addr(tree_addr, i); 221 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_set_tree_addr(tree_addr, tree); 222 223 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_copy_subtree_addr( 224 wots_addr, tree_addr); 225 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_set_keypair_addr( 226 wots_addr, idx_leaf); 227 228 /* Compute a WOTS signature. */ 229 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_wots_sign( 230 sig, root, sk_seed, pub_seed, wots_addr, &hash_state_seeded); 231 sig += PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_WOTS_BYTES; 232 233 /* Compute the authentication path for the used WOTS leaf. */ 234 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_treehash_TREE_HEIGHT( 235 root, sig, sk_seed, pub_seed, idx_leaf, 0, 236 wots_gen_leaf, tree_addr, &hash_state_seeded); 237 sig += PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_TREE_HEIGHT * PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N; 238 239 /* Update the indices for the next layer. */ 240 idx_leaf = (tree & ((1 << PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_TREE_HEIGHT) - 1)); 241 tree = tree >> PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_TREE_HEIGHT; 242 } 243 244 memcpy(orig_sig, aligned_sig.sig, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_BYTES); 245 *siglen = PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_BYTES; 246 247 return 0; 248 } 249 250 /** 251 * Verifies a detached signature and message under a given public key. 252 */ 253 int PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_crypto_sign_verify( 254 const uint8_t *sig, size_t siglen, 255 const uint8_t *m, size_t mlen, const uint8_t *pk) { 256 // guarantee alignment of pk 257 union { 258 __m128 *_x; 259 uint8_t pk[PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_CRYPTO_PUBLICKEYBYTES]; 260 } aligned_pk; 261 memcpy(aligned_pk.pk, pk, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_CRYPTO_PUBLICKEYBYTES); 262 pk = aligned_pk.pk; 263 264 const unsigned char *pub_seed = pk; 265 const unsigned char *pub_root = pk + PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N; 266 unsigned char mhash[PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_FORS_MSG_BYTES]; 267 unsigned char wots_pk[PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_WOTS_BYTES]; 268 unsigned char root[PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N]; 269 unsigned char leaf[PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N]; 270 unsigned int i; 271 uint64_t tree; 272 uint32_t idx_leaf; 273 uint32_t wots_addr[8] = {0}; 274 uint32_t tree_addr[8] = {0}; 275 uint32_t wots_pk_addr[8] = {0}; 276 277 hash_state hash_state_seeded; 278 279 if (siglen != PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_BYTES) { 280 return -1; 281 } 282 283 /* This hook allows the hash function instantiation to do whatever 284 preparation or computation it needs, based on the public seed. */ 285 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_initialize_hash_function( 286 &hash_state_seeded, 287 pub_seed, NULL); 288 289 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_set_type( 290 wots_addr, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_ADDR_TYPE_WOTS); 291 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_set_type( 292 tree_addr, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_ADDR_TYPE_HASHTREE); 293 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_set_type( 294 wots_pk_addr, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_ADDR_TYPE_WOTSPK); 295 296 /* Derive the message digest and leaf index from R || PK || M. */ 297 /* The additional PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N is a result of the hash domain separator. */ 298 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_hash_message( 299 mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); 300 sig += PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N; 301 302 /* Layer correctly defaults to 0, so no need to set_layer_addr */ 303 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_set_tree_addr(wots_addr, tree); 304 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_set_keypair_addr( 305 wots_addr, idx_leaf); 306 307 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_fors_pk_from_sig( 308 root, sig, mhash, pub_seed, wots_addr, &hash_state_seeded); 309 sig += PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_FORS_BYTES; 310 311 /* For each subtree.. */ 312 for (i = 0; i < PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_D; i++) { 313 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_set_layer_addr(tree_addr, i); 314 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_set_tree_addr(tree_addr, tree); 315 316 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_copy_subtree_addr( 317 wots_addr, tree_addr); 318 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_set_keypair_addr( 319 wots_addr, idx_leaf); 320 321 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_copy_keypair_addr( 322 wots_pk_addr, wots_addr); 323 324 /* The WOTS public key is only correct if the signature was correct. */ 325 /* Initially, root is the FORS pk, but on subsequent iterations it is 326 the root of the subtree below the currently processed subtree. */ 327 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_wots_pk_from_sig( 328 wots_pk, sig, root, pub_seed, wots_addr, &hash_state_seeded); 329 sig += PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_WOTS_BYTES; 330 331 /* Compute the leaf node using the WOTS public key. */ 332 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_thash_WOTS_LEN( 333 leaf, wots_pk, pub_seed, wots_pk_addr, &hash_state_seeded); 334 335 /* Compute the root node of this subtree. */ 336 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_compute_root( 337 root, leaf, idx_leaf, 0, sig, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_TREE_HEIGHT, 338 pub_seed, tree_addr, &hash_state_seeded); 339 sig += PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_TREE_HEIGHT * PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N; 340 341 /* Update the indices for the next layer. */ 342 idx_leaf = (tree & ((1 << PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_TREE_HEIGHT) - 1)); 343 tree = tree >> PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_TREE_HEIGHT; 344 } 345 346 /* Check if the root node equals the root node in the public key. */ 347 if (memcmp(root, pub_root, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_N) != 0) { 348 return -1; 349 } 350 351 return 0; 352 } 353 354 355 /** 356 * Returns an array containing the signature followed by the message. 357 */ 358 int PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_crypto_sign( 359 uint8_t *sm, size_t *smlen, 360 const uint8_t *m, size_t mlen, const uint8_t *sk) { 361 size_t siglen; 362 363 PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_crypto_sign_signature( 364 sm, &siglen, m, mlen, sk); 365 366 memmove(sm + PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_BYTES, m, mlen); 367 *smlen = siglen + mlen; 368 369 return 0; 370 } 371 372 /** 373 * Verifies a given signature-message pair under a given public key. 374 */ 375 int PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_crypto_sign_open( 376 uint8_t *m, size_t *mlen, 377 const uint8_t *sm, size_t smlen, const uint8_t *pk) { 378 379 // guarantee alignment of pk 380 union { 381 __m128 *_x; 382 uint8_t pk[PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_CRYPTO_PUBLICKEYBYTES]; 383 } aligned_pk; 384 memcpy(aligned_pk.pk, pk, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_CRYPTO_PUBLICKEYBYTES); 385 pk = aligned_pk.pk; 386 387 388 /* The API caller does not necessarily know what size a signature should be 389 but SPHINCS+ signatures are always exactly PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_BYTES. */ 390 if (smlen < PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_BYTES) { 391 memset(m, 0, smlen); 392 *mlen = 0; 393 return -1; 394 } 395 396 *mlen = smlen - PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_BYTES; 397 398 if (PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_crypto_sign_verify( 399 sm, PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_BYTES, sm + PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_BYTES, *mlen, pk)) { 400 memset(m, 0, smlen); 401 *mlen = 0; 402 return -1; 403 } 404 405 /* If verification was successful, move the message to the right place. */ 406 memmove(m, sm + PQCLEAN_SPHINCSSHAKE256192FROBUST_AVX2_BYTES, *mlen); 407 408 return 0; 409 } 410