@@ -3668,8 +3668,40 @@ bool ECKeyPointer::setPublicKey(const ECPointPointer& pub) {
36683668bool ECKeyPointer::setPublicKeyRaw (const BignumPointer& x,
36693669 const BignumPointer& y) {
36703670 if (!key_) return false ;
3671- return EC_KEY_set_public_key_affine_coordinates (
3672- key_.get (), x.get (), y.get ()) == 1 ;
3671+ const EC_GROUP* group = EC_KEY_get0_group (key_.get ());
3672+ if (group == nullptr ) return false ;
3673+
3674+ // For curves with cofactor h=1, use EC_POINT_oct2point +
3675+ // EC_KEY_set_public_key instead of
3676+ // EC_KEY_set_public_key_affine_coordinates.
3677+ // The latter internally calls EC_KEY_check_key() which performs a scalar
3678+ // multiplication (n*Q) for order validation — redundant when h=1 since every
3679+ // on-curve point already has order n. EC_POINT_oct2point validates the point
3680+ // is on the curve, which is sufficient. For curves with h!=1, fall back to
3681+ // the full check.
3682+ auto cofactor = BignumPointer::New ();
3683+ if (!cofactor || !EC_GROUP_get_cofactor (group, cofactor.get (), nullptr ) ||
3684+ !cofactor.isOne ()) {
3685+ return EC_KEY_set_public_key_affine_coordinates (
3686+ key_.get (), x.get (), y.get ()) == 1 ;
3687+ }
3688+
3689+ // Field element byte length: ceil(degree_bits / 8).
3690+ size_t field_len = (EC_GROUP_get_degree (group) + 7 ) / 8 ;
3691+
3692+ // Build an uncompressed point: 0x04 || x || y, each padded to field_len.
3693+ size_t uncompressed_len = 1 + 2 * field_len;
3694+ auto buf = DataPointer::Alloc (uncompressed_len);
3695+ if (!buf) return false ;
3696+ unsigned char * ptr = static_cast <unsigned char *>(buf.get ());
3697+ ptr[0 ] = POINT_CONVERSION_UNCOMPRESSED;
3698+ x.encodePaddedInto (ptr + 1 , field_len);
3699+ y.encodePaddedInto (ptr + 1 + field_len, field_len);
3700+
3701+ auto point = ECPointPointer::New (group);
3702+ if (!point) return false ;
3703+ if (!point.setFromBuffer ({ptr, uncompressed_len}, group)) return false ;
3704+ return EC_KEY_set_public_key (key_.get (), point.get ()) == 1 ;
36733705}
36743706
36753707bool ECKeyPointer::setPrivateKey (const BignumPointer& priv) {
@@ -3930,6 +3962,23 @@ bool EVPKeyCtxPointer::setSignatureMd(const EVPMDCtxPointer& md) {
39303962 1 ;
39313963}
39323964
3965+ bool EVPKeyCtxPointer::setSignatureMd (const Digest& md) {
3966+ if (!ctx_ || !md) return false ;
3967+ return EVP_PKEY_CTX_set_signature_md (ctx_.get (), md.get ()) == 1 ;
3968+ }
3969+
3970+ #if OPENSSL_VERSION_MAJOR >= 3
3971+ int EVPKeyCtxPointer::initForSignEx (const OSSL_PARAM params[]) {
3972+ if (!ctx_) return 0 ;
3973+ return EVP_PKEY_sign_init_ex (ctx_.get (), params);
3974+ }
3975+
3976+ int EVPKeyCtxPointer::initForVerifyEx (const OSSL_PARAM params[]) {
3977+ if (!ctx_) return 0 ;
3978+ return EVP_PKEY_verify_init_ex (ctx_.get (), params);
3979+ }
3980+ #endif
3981+
39333982bool EVPKeyCtxPointer::initForEncrypt () {
39343983 if (!ctx_) return false ;
39353984 return EVP_PKEY_encrypt_init (ctx_.get ()) == 1 ;
@@ -4491,6 +4540,27 @@ std::optional<EVP_PKEY_CTX*> EVPMDCtxPointer::signInitWithContext(
44914540#ifdef OSSL_SIGNATURE_PARAM_CONTEXT_STRING
44924541 EVP_PKEY_CTX* ctx = nullptr ;
44934542
4543+ #ifdef OSSL_SIGNATURE_PARAM_INSTANCE
4544+ // Ed25519ctx requires the INSTANCE param to enable context string support.
4545+ // Ed25519 pure mode ignores context strings without this.
4546+ if (key.id () == EVP_PKEY_ED25519) {
4547+ const OSSL_PARAM params[] = {
4548+ OSSL_PARAM_construct_utf8_string (
4549+ OSSL_SIGNATURE_PARAM_INSTANCE, const_cast <char *>(" Ed25519ctx" ), 0 ),
4550+ OSSL_PARAM_construct_octet_string (
4551+ OSSL_SIGNATURE_PARAM_CONTEXT_STRING,
4552+ const_cast <unsigned char *>(context_string.data ),
4553+ context_string.len ),
4554+ OSSL_PARAM_END};
4555+
4556+ if (!EVP_DigestSignInit_ex (
4557+ ctx_.get (), &ctx, nullptr , nullptr , nullptr , key.get (), params)) {
4558+ return std::nullopt ;
4559+ }
4560+ return ctx;
4561+ }
4562+ #endif // OSSL_SIGNATURE_PARAM_INSTANCE
4563+
44944564 const OSSL_PARAM params[] = {
44954565 OSSL_PARAM_construct_octet_string (
44964566 OSSL_SIGNATURE_PARAM_CONTEXT_STRING,
@@ -4515,6 +4585,27 @@ std::optional<EVP_PKEY_CTX*> EVPMDCtxPointer::verifyInitWithContext(
45154585#ifdef OSSL_SIGNATURE_PARAM_CONTEXT_STRING
45164586 EVP_PKEY_CTX* ctx = nullptr ;
45174587
4588+ #ifdef OSSL_SIGNATURE_PARAM_INSTANCE
4589+ // Ed25519ctx requires the INSTANCE param to enable context string support.
4590+ // Ed25519 pure mode ignores context strings without this.
4591+ if (key.id () == EVP_PKEY_ED25519) {
4592+ const OSSL_PARAM params[] = {
4593+ OSSL_PARAM_construct_utf8_string (
4594+ OSSL_SIGNATURE_PARAM_INSTANCE, const_cast <char *>(" Ed25519ctx" ), 0 ),
4595+ OSSL_PARAM_construct_octet_string (
4596+ OSSL_SIGNATURE_PARAM_CONTEXT_STRING,
4597+ const_cast <unsigned char *>(context_string.data ),
4598+ context_string.len ),
4599+ OSSL_PARAM_END};
4600+
4601+ if (!EVP_DigestVerifyInit_ex (
4602+ ctx_.get (), &ctx, nullptr , nullptr , nullptr , key.get (), params)) {
4603+ return std::nullopt ;
4604+ }
4605+ return ctx;
4606+ }
4607+ #endif // OSSL_SIGNATURE_PARAM_INSTANCE
4608+
45184609 const OSSL_PARAM params[] = {
45194610 OSSL_PARAM_construct_octet_string (
45204611 OSSL_SIGNATURE_PARAM_CONTEXT_STRING,
0 commit comments