/* * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #include #include "crypto/lms.h" #include "crypto/lms_util.h" /** * @brief Calculate the size of a public key in XDR format. * * @param data A byte array of XDR data for a LMS public key. * The first 4 bytes are looked at. * @param datalen The size of |data|. * @returns The calculated size, or 0 on error. */ size_t ossl_lms_pubkey_length(const unsigned char *data, size_t datalen) { PACKET pkt; uint32_t lms_type; const LMS_PARAMS *params; if (!PACKET_buf_init(&pkt, data, datalen) || !PACKET_get_net_4_len_u32(&pkt, &lms_type) || (params = ossl_lms_params_get(lms_type)) == NULL) return 0; return LMS_SIZE_LMS_TYPE + LMS_SIZE_OTS_TYPE + LMS_SIZE_I + params->n; } /* * @brief Decode LMS public key data in XDR format into a LMS_KEY object. * * See RFC 8554 Algorithm 6: Steps 1 & 2. * The XDR format is lms_type[4] || ots_type[4] || I[16] || K[n] * Steps that involve checking the size of the public key data are * done indirectly by checking the return result of PACKET_get API's. * This function may be called multiple times. * * @param pkt The packet to read public key data in XDR format from. * @param lmskey The object to store the public key into * @return 1 on success or 0 otherwise. */ static int lms_pubkey_from_pkt(LMS_KEY *lmskey, const unsigned char *pubkeydata, size_t publen) { uint32_t lms_type; uint32_t ots_type; LMS_PUB_KEY *key = &lmskey->pub; PACKET pkt; if (!PACKET_buf_init(&pkt, pubkeydata, publen)) goto err; key->encoded = (unsigned char *)PACKET_data(&pkt); if (!PACKET_get_net_4_len_u32(&pkt, &lms_type)) goto err; lmskey->lms_params = ossl_lms_params_get(lms_type); if (lmskey->lms_params == NULL || !PACKET_get_net_4_len_u32(&pkt, &ots_type)) goto err; lmskey->ots_params = ossl_lm_ots_params_get(ots_type); if (lmskey->ots_params == NULL) goto err; /* The digest used must be the same */ if (HASH_NOT_MATCHED(lmskey->ots_params, lmskey->lms_params) || !PACKET_get_bytes(&pkt, (const unsigned char **)&lmskey->Id, LMS_SIZE_I) || !PACKET_get_bytes(&pkt, (const unsigned char **)&key->K, lmskey->lms_params->n)) goto err; key->encodedlen = (unsigned char *)PACKET_data(&pkt) - key->encoded; return PACKET_remaining(&pkt) == 0; err: return 0; } /* * @brief Decode LMS public key data in XDR format into a LMS_KEY object. * Used by the LMS public key decoder. * The XDR format is lms_type[4] || ots_type[4] || I[16] || K[n] * * @param pub byte array of public key data in XDR format. * @param publen is the size of |pub|. * @param lmskey The LMS_KEY object to store the public key into. * @returns 1 on success, or 0 otherwise. 0 is returned if either |pub| is * invalid or |publen| is not the correct size (i.e. trailing data is not allowed) */ int ossl_lms_pubkey_decode(const unsigned char *pub, size_t publen, LMS_KEY *lmskey) { LMS_PUB_KEY *pkey = &lmskey->pub; if (pkey->encoded != NULL && pkey->encodedlen != publen) { OPENSSL_free(pkey->encoded); pkey->encodedlen = 0; } pkey->encoded = OPENSSL_memdup(pub, publen); if (pkey->encoded == NULL) return 0; if (!lms_pubkey_from_pkt(lmskey, pkey->encoded, publen)) goto err; pkey->encodedlen = publen; return 1; err: OPENSSL_free(pkey->encoded); pkey->encoded = NULL; return 0; } /** * @brief Load a LMS public key from OSSL_PARAM data. * * @param params An array of OSSL_PARAM * @param lmskey The LMS_KEY to load the public key data into. * @returns 1 on success, or 0 otherwise. */ int ossl_lms_pubkey_from_params(const OSSL_PARAM *pub, LMS_KEY *lmskey) { if (pub != NULL) { if (pub->data == NULL || pub->data_type != OSSL_PARAM_OCTET_STRING || !ossl_lms_pubkey_decode(pub->data, pub->data_size, lmskey)) return 0; } return 1; }