1*b077aed3SPierre Pronchery /* 2*b077aed3SPierre Pronchery * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved. 3*b077aed3SPierre Pronchery * 4*b077aed3SPierre Pronchery * Licensed under the Apache License 2.0 (the "License"). You may not use 5*b077aed3SPierre Pronchery * this file except in compliance with the License. You can obtain a copy 6*b077aed3SPierre Pronchery * in the file LICENSE in the source distribution or at 7*b077aed3SPierre Pronchery * https://www.openssl.org/source/license.html 8*b077aed3SPierre Pronchery */ 9*b077aed3SPierre Pronchery 10*b077aed3SPierre Pronchery /* We need to use some engine deprecated APIs */ 11*b077aed3SPierre Pronchery #define OPENSSL_SUPPRESS_DEPRECATED 12*b077aed3SPierre Pronchery 13*b077aed3SPierre Pronchery #include <openssl/evp.h> 14*b077aed3SPierre Pronchery #include <openssl/core_names.h> 15*b077aed3SPierre Pronchery #include <openssl/err.h> 16*b077aed3SPierre Pronchery #include <openssl/proverr.h> 17*b077aed3SPierre Pronchery #ifndef FIPS_MODULE 18*b077aed3SPierre Pronchery # include <openssl/engine.h> 19*b077aed3SPierre Pronchery # include "crypto/evp.h" 20*b077aed3SPierre Pronchery #endif 21*b077aed3SPierre Pronchery #include "prov/provider_util.h" 22*b077aed3SPierre Pronchery #include "internal/nelem.h" 23*b077aed3SPierre Pronchery 24*b077aed3SPierre Pronchery void ossl_prov_cipher_reset(PROV_CIPHER *pc) 25*b077aed3SPierre Pronchery { 26*b077aed3SPierre Pronchery EVP_CIPHER_free(pc->alloc_cipher); 27*b077aed3SPierre Pronchery pc->alloc_cipher = NULL; 28*b077aed3SPierre Pronchery pc->cipher = NULL; 29*b077aed3SPierre Pronchery #if !defined(FIPS_MODULE) && !defined(OPENSSL_NO_ENGINE) 30*b077aed3SPierre Pronchery ENGINE_finish(pc->engine); 31*b077aed3SPierre Pronchery #endif 32*b077aed3SPierre Pronchery pc->engine = NULL; 33*b077aed3SPierre Pronchery } 34*b077aed3SPierre Pronchery 35*b077aed3SPierre Pronchery int ossl_prov_cipher_copy(PROV_CIPHER *dst, const PROV_CIPHER *src) 36*b077aed3SPierre Pronchery { 37*b077aed3SPierre Pronchery if (src->alloc_cipher != NULL && !EVP_CIPHER_up_ref(src->alloc_cipher)) 38*b077aed3SPierre Pronchery return 0; 39*b077aed3SPierre Pronchery #if !defined(FIPS_MODULE) && !defined(OPENSSL_NO_ENGINE) 40*b077aed3SPierre Pronchery if (src->engine != NULL && !ENGINE_init(src->engine)) { 41*b077aed3SPierre Pronchery EVP_CIPHER_free(src->alloc_cipher); 42*b077aed3SPierre Pronchery return 0; 43*b077aed3SPierre Pronchery } 44*b077aed3SPierre Pronchery #endif 45*b077aed3SPierre Pronchery dst->engine = src->engine; 46*b077aed3SPierre Pronchery dst->cipher = src->cipher; 47*b077aed3SPierre Pronchery dst->alloc_cipher = src->alloc_cipher; 48*b077aed3SPierre Pronchery return 1; 49*b077aed3SPierre Pronchery } 50*b077aed3SPierre Pronchery 51*b077aed3SPierre Pronchery static int load_common(const OSSL_PARAM params[], const char **propquery, 52*b077aed3SPierre Pronchery ENGINE **engine) 53*b077aed3SPierre Pronchery { 54*b077aed3SPierre Pronchery const OSSL_PARAM *p; 55*b077aed3SPierre Pronchery 56*b077aed3SPierre Pronchery *propquery = NULL; 57*b077aed3SPierre Pronchery p = OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_PROPERTIES); 58*b077aed3SPierre Pronchery if (p != NULL) { 59*b077aed3SPierre Pronchery if (p->data_type != OSSL_PARAM_UTF8_STRING) 60*b077aed3SPierre Pronchery return 0; 61*b077aed3SPierre Pronchery *propquery = p->data; 62*b077aed3SPierre Pronchery } 63*b077aed3SPierre Pronchery 64*b077aed3SPierre Pronchery #if !defined(FIPS_MODULE) && !defined(OPENSSL_NO_ENGINE) 65*b077aed3SPierre Pronchery ENGINE_finish(*engine); 66*b077aed3SPierre Pronchery #endif 67*b077aed3SPierre Pronchery *engine = NULL; 68*b077aed3SPierre Pronchery /* Inside the FIPS module, we don't support legacy ciphers */ 69*b077aed3SPierre Pronchery #if !defined(FIPS_MODULE) && !defined(OPENSSL_NO_ENGINE) 70*b077aed3SPierre Pronchery p = OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_ENGINE); 71*b077aed3SPierre Pronchery if (p != NULL) { 72*b077aed3SPierre Pronchery if (p->data_type != OSSL_PARAM_UTF8_STRING) 73*b077aed3SPierre Pronchery return 0; 74*b077aed3SPierre Pronchery /* Get a structural reference */ 75*b077aed3SPierre Pronchery *engine = ENGINE_by_id(p->data); 76*b077aed3SPierre Pronchery if (*engine == NULL) 77*b077aed3SPierre Pronchery return 0; 78*b077aed3SPierre Pronchery /* Get a functional reference */ 79*b077aed3SPierre Pronchery if (!ENGINE_init(*engine)) { 80*b077aed3SPierre Pronchery ENGINE_free(*engine); 81*b077aed3SPierre Pronchery *engine = NULL; 82*b077aed3SPierre Pronchery return 0; 83*b077aed3SPierre Pronchery } 84*b077aed3SPierre Pronchery /* Free the structural reference */ 85*b077aed3SPierre Pronchery ENGINE_free(*engine); 86*b077aed3SPierre Pronchery } 87*b077aed3SPierre Pronchery #endif 88*b077aed3SPierre Pronchery return 1; 89*b077aed3SPierre Pronchery } 90*b077aed3SPierre Pronchery 91*b077aed3SPierre Pronchery int ossl_prov_cipher_load_from_params(PROV_CIPHER *pc, 92*b077aed3SPierre Pronchery const OSSL_PARAM params[], 93*b077aed3SPierre Pronchery OSSL_LIB_CTX *ctx) 94*b077aed3SPierre Pronchery { 95*b077aed3SPierre Pronchery const OSSL_PARAM *p; 96*b077aed3SPierre Pronchery const char *propquery; 97*b077aed3SPierre Pronchery 98*b077aed3SPierre Pronchery if (params == NULL) 99*b077aed3SPierre Pronchery return 1; 100*b077aed3SPierre Pronchery 101*b077aed3SPierre Pronchery if (!load_common(params, &propquery, &pc->engine)) 102*b077aed3SPierre Pronchery return 0; 103*b077aed3SPierre Pronchery 104*b077aed3SPierre Pronchery p = OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_CIPHER); 105*b077aed3SPierre Pronchery if (p == NULL) 106*b077aed3SPierre Pronchery return 1; 107*b077aed3SPierre Pronchery if (p->data_type != OSSL_PARAM_UTF8_STRING) 108*b077aed3SPierre Pronchery return 0; 109*b077aed3SPierre Pronchery 110*b077aed3SPierre Pronchery EVP_CIPHER_free(pc->alloc_cipher); 111*b077aed3SPierre Pronchery ERR_set_mark(); 112*b077aed3SPierre Pronchery pc->cipher = pc->alloc_cipher = EVP_CIPHER_fetch(ctx, p->data, propquery); 113*b077aed3SPierre Pronchery #ifndef FIPS_MODULE /* Inside the FIPS module, we don't support legacy ciphers */ 114*b077aed3SPierre Pronchery if (pc->cipher == NULL) { 115*b077aed3SPierre Pronchery const EVP_CIPHER *cipher; 116*b077aed3SPierre Pronchery 117*b077aed3SPierre Pronchery cipher = EVP_get_cipherbyname(p->data); 118*b077aed3SPierre Pronchery /* Do not use global EVP_CIPHERs */ 119*b077aed3SPierre Pronchery if (cipher != NULL && cipher->origin != EVP_ORIG_GLOBAL) 120*b077aed3SPierre Pronchery pc->cipher = cipher; 121*b077aed3SPierre Pronchery } 122*b077aed3SPierre Pronchery #endif 123*b077aed3SPierre Pronchery if (pc->cipher != NULL) 124*b077aed3SPierre Pronchery ERR_pop_to_mark(); 125*b077aed3SPierre Pronchery else 126*b077aed3SPierre Pronchery ERR_clear_last_mark(); 127*b077aed3SPierre Pronchery return pc->cipher != NULL; 128*b077aed3SPierre Pronchery } 129*b077aed3SPierre Pronchery 130*b077aed3SPierre Pronchery const EVP_CIPHER *ossl_prov_cipher_cipher(const PROV_CIPHER *pc) 131*b077aed3SPierre Pronchery { 132*b077aed3SPierre Pronchery return pc->cipher; 133*b077aed3SPierre Pronchery } 134*b077aed3SPierre Pronchery 135*b077aed3SPierre Pronchery ENGINE *ossl_prov_cipher_engine(const PROV_CIPHER *pc) 136*b077aed3SPierre Pronchery { 137*b077aed3SPierre Pronchery return pc->engine; 138*b077aed3SPierre Pronchery } 139*b077aed3SPierre Pronchery 140*b077aed3SPierre Pronchery void ossl_prov_digest_reset(PROV_DIGEST *pd) 141*b077aed3SPierre Pronchery { 142*b077aed3SPierre Pronchery EVP_MD_free(pd->alloc_md); 143*b077aed3SPierre Pronchery pd->alloc_md = NULL; 144*b077aed3SPierre Pronchery pd->md = NULL; 145*b077aed3SPierre Pronchery #if !defined(FIPS_MODULE) && !defined(OPENSSL_NO_ENGINE) 146*b077aed3SPierre Pronchery ENGINE_finish(pd->engine); 147*b077aed3SPierre Pronchery #endif 148*b077aed3SPierre Pronchery pd->engine = NULL; 149*b077aed3SPierre Pronchery } 150*b077aed3SPierre Pronchery 151*b077aed3SPierre Pronchery int ossl_prov_digest_copy(PROV_DIGEST *dst, const PROV_DIGEST *src) 152*b077aed3SPierre Pronchery { 153*b077aed3SPierre Pronchery if (src->alloc_md != NULL && !EVP_MD_up_ref(src->alloc_md)) 154*b077aed3SPierre Pronchery return 0; 155*b077aed3SPierre Pronchery #if !defined(FIPS_MODULE) && !defined(OPENSSL_NO_ENGINE) 156*b077aed3SPierre Pronchery if (src->engine != NULL && !ENGINE_init(src->engine)) { 157*b077aed3SPierre Pronchery EVP_MD_free(src->alloc_md); 158*b077aed3SPierre Pronchery return 0; 159*b077aed3SPierre Pronchery } 160*b077aed3SPierre Pronchery #endif 161*b077aed3SPierre Pronchery dst->engine = src->engine; 162*b077aed3SPierre Pronchery dst->md = src->md; 163*b077aed3SPierre Pronchery dst->alloc_md = src->alloc_md; 164*b077aed3SPierre Pronchery return 1; 165*b077aed3SPierre Pronchery } 166*b077aed3SPierre Pronchery 167*b077aed3SPierre Pronchery const EVP_MD *ossl_prov_digest_fetch(PROV_DIGEST *pd, OSSL_LIB_CTX *libctx, 168*b077aed3SPierre Pronchery const char *mdname, const char *propquery) 169*b077aed3SPierre Pronchery { 170*b077aed3SPierre Pronchery EVP_MD_free(pd->alloc_md); 171*b077aed3SPierre Pronchery pd->md = pd->alloc_md = EVP_MD_fetch(libctx, mdname, propquery); 172*b077aed3SPierre Pronchery 173*b077aed3SPierre Pronchery return pd->md; 174*b077aed3SPierre Pronchery } 175*b077aed3SPierre Pronchery 176*b077aed3SPierre Pronchery int ossl_prov_digest_load_from_params(PROV_DIGEST *pd, 177*b077aed3SPierre Pronchery const OSSL_PARAM params[], 178*b077aed3SPierre Pronchery OSSL_LIB_CTX *ctx) 179*b077aed3SPierre Pronchery { 180*b077aed3SPierre Pronchery const OSSL_PARAM *p; 181*b077aed3SPierre Pronchery const char *propquery; 182*b077aed3SPierre Pronchery 183*b077aed3SPierre Pronchery if (params == NULL) 184*b077aed3SPierre Pronchery return 1; 185*b077aed3SPierre Pronchery 186*b077aed3SPierre Pronchery if (!load_common(params, &propquery, &pd->engine)) 187*b077aed3SPierre Pronchery return 0; 188*b077aed3SPierre Pronchery 189*b077aed3SPierre Pronchery p = OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST); 190*b077aed3SPierre Pronchery if (p == NULL) 191*b077aed3SPierre Pronchery return 1; 192*b077aed3SPierre Pronchery if (p->data_type != OSSL_PARAM_UTF8_STRING) 193*b077aed3SPierre Pronchery return 0; 194*b077aed3SPierre Pronchery 195*b077aed3SPierre Pronchery ERR_set_mark(); 196*b077aed3SPierre Pronchery ossl_prov_digest_fetch(pd, ctx, p->data, propquery); 197*b077aed3SPierre Pronchery #ifndef FIPS_MODULE /* Inside the FIPS module, we don't support legacy digests */ 198*b077aed3SPierre Pronchery if (pd->md == NULL) { 199*b077aed3SPierre Pronchery const EVP_MD *md; 200*b077aed3SPierre Pronchery 201*b077aed3SPierre Pronchery md = EVP_get_digestbyname(p->data); 202*b077aed3SPierre Pronchery /* Do not use global EVP_MDs */ 203*b077aed3SPierre Pronchery if (md != NULL && md->origin != EVP_ORIG_GLOBAL) 204*b077aed3SPierre Pronchery pd->md = md; 205*b077aed3SPierre Pronchery } 206*b077aed3SPierre Pronchery #endif 207*b077aed3SPierre Pronchery if (pd->md != NULL) 208*b077aed3SPierre Pronchery ERR_pop_to_mark(); 209*b077aed3SPierre Pronchery else 210*b077aed3SPierre Pronchery ERR_clear_last_mark(); 211*b077aed3SPierre Pronchery return pd->md != NULL; 212*b077aed3SPierre Pronchery } 213*b077aed3SPierre Pronchery 214*b077aed3SPierre Pronchery const EVP_MD *ossl_prov_digest_md(const PROV_DIGEST *pd) 215*b077aed3SPierre Pronchery { 216*b077aed3SPierre Pronchery return pd->md; 217*b077aed3SPierre Pronchery } 218*b077aed3SPierre Pronchery 219*b077aed3SPierre Pronchery ENGINE *ossl_prov_digest_engine(const PROV_DIGEST *pd) 220*b077aed3SPierre Pronchery { 221*b077aed3SPierre Pronchery return pd->engine; 222*b077aed3SPierre Pronchery } 223*b077aed3SPierre Pronchery 224*b077aed3SPierre Pronchery int ossl_prov_set_macctx(EVP_MAC_CTX *macctx, 225*b077aed3SPierre Pronchery const OSSL_PARAM params[], 226*b077aed3SPierre Pronchery const char *ciphername, 227*b077aed3SPierre Pronchery const char *mdname, 228*b077aed3SPierre Pronchery const char *engine, 229*b077aed3SPierre Pronchery const char *properties, 230*b077aed3SPierre Pronchery const unsigned char *key, 231*b077aed3SPierre Pronchery size_t keylen) 232*b077aed3SPierre Pronchery { 233*b077aed3SPierre Pronchery const OSSL_PARAM *p; 234*b077aed3SPierre Pronchery OSSL_PARAM mac_params[6], *mp = mac_params; 235*b077aed3SPierre Pronchery 236*b077aed3SPierre Pronchery if (params != NULL) { 237*b077aed3SPierre Pronchery if (mdname == NULL) { 238*b077aed3SPierre Pronchery if ((p = OSSL_PARAM_locate_const(params, 239*b077aed3SPierre Pronchery OSSL_ALG_PARAM_DIGEST)) != NULL) { 240*b077aed3SPierre Pronchery if (p->data_type != OSSL_PARAM_UTF8_STRING) 241*b077aed3SPierre Pronchery return 0; 242*b077aed3SPierre Pronchery mdname = p->data; 243*b077aed3SPierre Pronchery } 244*b077aed3SPierre Pronchery } 245*b077aed3SPierre Pronchery if (ciphername == NULL) { 246*b077aed3SPierre Pronchery if ((p = OSSL_PARAM_locate_const(params, 247*b077aed3SPierre Pronchery OSSL_ALG_PARAM_CIPHER)) != NULL) { 248*b077aed3SPierre Pronchery if (p->data_type != OSSL_PARAM_UTF8_STRING) 249*b077aed3SPierre Pronchery return 0; 250*b077aed3SPierre Pronchery ciphername = p->data; 251*b077aed3SPierre Pronchery } 252*b077aed3SPierre Pronchery } 253*b077aed3SPierre Pronchery if (engine == NULL) { 254*b077aed3SPierre Pronchery if ((p = OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_ENGINE)) 255*b077aed3SPierre Pronchery != NULL) { 256*b077aed3SPierre Pronchery if (p->data_type != OSSL_PARAM_UTF8_STRING) 257*b077aed3SPierre Pronchery return 0; 258*b077aed3SPierre Pronchery engine = p->data; 259*b077aed3SPierre Pronchery } 260*b077aed3SPierre Pronchery } 261*b077aed3SPierre Pronchery } 262*b077aed3SPierre Pronchery 263*b077aed3SPierre Pronchery if (mdname != NULL) 264*b077aed3SPierre Pronchery *mp++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, 265*b077aed3SPierre Pronchery (char *)mdname, 0); 266*b077aed3SPierre Pronchery if (ciphername != NULL) 267*b077aed3SPierre Pronchery *mp++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_CIPHER, 268*b077aed3SPierre Pronchery (char *)ciphername, 0); 269*b077aed3SPierre Pronchery if (properties != NULL) 270*b077aed3SPierre Pronchery *mp++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_PROPERTIES, 271*b077aed3SPierre Pronchery (char *)properties, 0); 272*b077aed3SPierre Pronchery 273*b077aed3SPierre Pronchery #if !defined(OPENSSL_NO_ENGINE) && !defined(FIPS_MODULE) 274*b077aed3SPierre Pronchery if (engine != NULL) 275*b077aed3SPierre Pronchery *mp++ = OSSL_PARAM_construct_utf8_string(OSSL_ALG_PARAM_ENGINE, 276*b077aed3SPierre Pronchery (char *) engine, 0); 277*b077aed3SPierre Pronchery #endif 278*b077aed3SPierre Pronchery 279*b077aed3SPierre Pronchery if (key != NULL) 280*b077aed3SPierre Pronchery *mp++ = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY, 281*b077aed3SPierre Pronchery (unsigned char *)key, 282*b077aed3SPierre Pronchery keylen); 283*b077aed3SPierre Pronchery 284*b077aed3SPierre Pronchery *mp = OSSL_PARAM_construct_end(); 285*b077aed3SPierre Pronchery 286*b077aed3SPierre Pronchery return EVP_MAC_CTX_set_params(macctx, mac_params); 287*b077aed3SPierre Pronchery 288*b077aed3SPierre Pronchery } 289*b077aed3SPierre Pronchery 290*b077aed3SPierre Pronchery int ossl_prov_macctx_load_from_params(EVP_MAC_CTX **macctx, 291*b077aed3SPierre Pronchery const OSSL_PARAM params[], 292*b077aed3SPierre Pronchery const char *macname, 293*b077aed3SPierre Pronchery const char *ciphername, 294*b077aed3SPierre Pronchery const char *mdname, 295*b077aed3SPierre Pronchery OSSL_LIB_CTX *libctx) 296*b077aed3SPierre Pronchery { 297*b077aed3SPierre Pronchery const OSSL_PARAM *p; 298*b077aed3SPierre Pronchery const char *properties = NULL; 299*b077aed3SPierre Pronchery 300*b077aed3SPierre Pronchery if (macname == NULL 301*b077aed3SPierre Pronchery && (p = OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_MAC)) != NULL) { 302*b077aed3SPierre Pronchery if (p->data_type != OSSL_PARAM_UTF8_STRING) 303*b077aed3SPierre Pronchery return 0; 304*b077aed3SPierre Pronchery macname = p->data; 305*b077aed3SPierre Pronchery } 306*b077aed3SPierre Pronchery if ((p = OSSL_PARAM_locate_const(params, 307*b077aed3SPierre Pronchery OSSL_ALG_PARAM_PROPERTIES)) != NULL) { 308*b077aed3SPierre Pronchery if (p->data_type != OSSL_PARAM_UTF8_STRING) 309*b077aed3SPierre Pronchery return 0; 310*b077aed3SPierre Pronchery properties = p->data; 311*b077aed3SPierre Pronchery } 312*b077aed3SPierre Pronchery 313*b077aed3SPierre Pronchery /* If we got a new mac name, we make a new EVP_MAC_CTX */ 314*b077aed3SPierre Pronchery if (macname != NULL) { 315*b077aed3SPierre Pronchery EVP_MAC *mac = EVP_MAC_fetch(libctx, macname, properties); 316*b077aed3SPierre Pronchery 317*b077aed3SPierre Pronchery EVP_MAC_CTX_free(*macctx); 318*b077aed3SPierre Pronchery *macctx = mac == NULL ? NULL : EVP_MAC_CTX_new(mac); 319*b077aed3SPierre Pronchery /* The context holds on to the MAC */ 320*b077aed3SPierre Pronchery EVP_MAC_free(mac); 321*b077aed3SPierre Pronchery if (*macctx == NULL) 322*b077aed3SPierre Pronchery return 0; 323*b077aed3SPierre Pronchery } 324*b077aed3SPierre Pronchery 325*b077aed3SPierre Pronchery /* 326*b077aed3SPierre Pronchery * If there is no MAC yet (and therefore, no MAC context), we ignore 327*b077aed3SPierre Pronchery * all other parameters. 328*b077aed3SPierre Pronchery */ 329*b077aed3SPierre Pronchery if (*macctx == NULL) 330*b077aed3SPierre Pronchery return 1; 331*b077aed3SPierre Pronchery 332*b077aed3SPierre Pronchery if (ossl_prov_set_macctx(*macctx, params, ciphername, mdname, NULL, 333*b077aed3SPierre Pronchery properties, NULL, 0)) 334*b077aed3SPierre Pronchery return 1; 335*b077aed3SPierre Pronchery 336*b077aed3SPierre Pronchery EVP_MAC_CTX_free(*macctx); 337*b077aed3SPierre Pronchery *macctx = NULL; 338*b077aed3SPierre Pronchery return 0; 339*b077aed3SPierre Pronchery } 340*b077aed3SPierre Pronchery 341*b077aed3SPierre Pronchery void ossl_prov_cache_exported_algorithms(const OSSL_ALGORITHM_CAPABLE *in, 342*b077aed3SPierre Pronchery OSSL_ALGORITHM *out) 343*b077aed3SPierre Pronchery { 344*b077aed3SPierre Pronchery int i, j; 345*b077aed3SPierre Pronchery 346*b077aed3SPierre Pronchery if (out[0].algorithm_names == NULL) { 347*b077aed3SPierre Pronchery for (i = j = 0; in[i].alg.algorithm_names != NULL; ++i) { 348*b077aed3SPierre Pronchery if (in[i].capable == NULL || in[i].capable()) 349*b077aed3SPierre Pronchery out[j++] = in[i].alg; 350*b077aed3SPierre Pronchery } 351*b077aed3SPierre Pronchery out[j++] = in[i].alg; 352*b077aed3SPierre Pronchery } 353*b077aed3SPierre Pronchery } 354