pkey_op = EVP_PKEY_OP_DECAPSULATE;
break;
case OPT_ENCAP:
+ key_type = KEY_PUBKEY;
pkey_op = EVP_PKEY_OP_ENCAPSULATE;
break;
case OPT_KEMOP:
if (in == NULL)
goto end;
}
- out = bio_open_default(outfile, 'w', FORMAT_BINARY);
- if (out == NULL)
- goto end;
+ if (pkey_op == EVP_PKEY_OP_DECAPSULATE && outfile != NULL) {
+ if (secoutfile != NULL) {
+ BIO_printf(bio_err, "%s: Decapsulation produces only a shared "
+ "secret and no output. The '-out' option "
+ "is not applicable.\n", prog);
+ goto end;
+ }
+ if ((out = bio_open_owner(outfile, 'w', FORMAT_BINARY)) == NULL)
+ goto end;
+ } else {
+ out = bio_open_default(outfile, 'w', FORMAT_BINARY);
+ if (out == NULL)
+ goto end;
+ }
- if (pkey_op == EVP_PKEY_OP_ENCAPSULATE) {
- if (secoutfile == NULL) {
- BIO_printf(bio_err, "Encapsulation requires '-secret' argument\n");
+ if (pkey_op == EVP_PKEY_OP_ENCAPSULATE
+ || pkey_op == EVP_PKEY_OP_DECAPSULATE) {
+ if (secoutfile == NULL && pkey_op == EVP_PKEY_OP_ENCAPSULATE) {
+ BIO_printf(bio_err, "KEM-based shared-secret derivation requires "
+ "the '-secret <file>' option\n");
goto end;
}
- secout = bio_open_default(secoutfile, 'w', FORMAT_BINARY);
- if (secout == NULL)
+ /* For backwards compatibility, default decap secrets to the output */
+ if (secoutfile != NULL
+ && (secout = bio_open_owner(secoutfile, 'w', FORMAT_BINARY)) == NULL)
goto end;
}
rv = do_keyop(ctx, pkey_op, NULL, (size_t *)&buf_outlen,
buf_in, (size_t)buf_inlen, NULL, (size_t *)&secretlen);
}
- if (rv > 0 && buf_outlen != 0) {
- buf_out = app_malloc(buf_outlen, "buffer output");
+ if (rv > 0
+ && (secretlen > 0 || (pkey_op != EVP_PKEY_OP_ENCAPSULATE
+ && pkey_op != EVP_PKEY_OP_DECAPSULATE))
+ && (buf_outlen > 0 || pkey_op == EVP_PKEY_OP_DECAPSULATE)) {
+ if (buf_outlen > 0)
+ buf_out = app_malloc(buf_outlen, "buffer output");
if (secretlen > 0)
secret = app_malloc(secretlen, "secret output");
rv = do_keyop(ctx, pkey_op,
} else {
BIO_write(out, buf_out, buf_outlen);
}
+ /* Backwards compatible decap output fallback */
if (secretlen > 0)
- BIO_write(secout, secret, secretlen);
+ BIO_write(secout ? secout : out, secret, secretlen);
end:
if (ret != 0)
break;
case EVP_PKEY_OP_DECAPSULATE:
- rv = EVP_PKEY_decapsulate(ctx, out, poutlen, in, inlen);
+ rv = EVP_PKEY_decapsulate(ctx, secret, pseclen, in, inlen);
break;
}
=item B<-secret> I<filename>
-Specifies the output filename to write the secret to on I<-encap>.
+Specifies the shared-secret output filename for when performing encapsulation
+via the B<-encap> option or decapsulation via the B<-decap> option.
+The B<-encap> option also produces a separate (public) ciphertext output which
+is by default written to standard output, but being I<binary> non-text data,
+is typically also redirected to a file selected via the I<-out> option.
=item B<-sigfile> I<file>
=item B<-encap>
-Encapsulate a generated secret using a private key.
-The encapsulated result (binary data) is written to standard output by default,
-or else to the file specified with I<-out>.
+Use a Key Encapsulation Mechanism (B<KEM>) to B<encapsulate> a shared-secret to
+a peer's B<public> key.
+The encapsulated result (or ciphertext, non-text binary data) is written to
+standard output by default, or else to the file specified with I<-out>.
The I<-secret> option must also be provided to specify the output file for the
-secret value generated in the encapsulation process.
+derived shared-secret value generated in the encapsulation process.
+Encapsulation is supported with a number of public key algorithms, currently:
+L<ML-KEM|EVP_PKEY-ML-KEM(7)>,
+L<X25519|EVP_KEM-X25519(7)>,
+L<X449|EVP_KEM-X448(7)>,
+and
+L<EC|EVP_KEM-EC(7)>.
+The ECX and EC algorithms use the
+L<RFC9180|https://www.rfc-editor.org/rfc/rfc9180> DHKEM construction.
+Encapsulation is also supported with L<RSA|EVP_KEM-RSA(7)> keys with the use of
+an additional B<-kemop> option.
+
+At the API level, encapsulation and decapsulation are also supported for a few
+hybrid ECDHE (no DHKEM) plus B<ML-KEM> algorithms, but these are intended
+primarily for use with TLS and should not be used standalone.
+There are in any case no standard public and private key formats for the hybrid
+algorithms, so it is not possible to provide the required key material.
=item B<-decap>
-Decapsulate the secret using a private key.
-The result (binary data) is written to standard output by default, or else to
-the file specified with I<-out>.
+Decode an encapsulated secret, with the use of a B<-private> key, to derive the
+same shared-secret as that obtained when the secret was encapsulated to the
+corresponding public key.
+The encapsulated secret is by default read from the standard input, or else
+from the file specified with B<-in>.
+The derived shared-secret is written to the file specified with the B<-secret>
+option, which I<must> also be provided.
+Decapsulation is supported with a number of public key algorithms, currently:
+L<ML-KEM|EVP_PKEY-ML-KEM(7)>,
+L<X25519|EVP_KEM-X25519(7)>,
+L<X448|EVP_KEM-X448(7)>,
+and
+L<EC|EVP_KEM-EC(7)>.
+The ECX and EC algorithms use the
+L<RFC9180|https://www.rfc-editor.org/rfc/rfc9180> DHKEM construction.
+Encapsulation is also supported with L<RSA|EVP_KEM-RSA(7)> keys with the use of
+an additional B<-kemop> option.
=item B<-kemop> I<operation>
-This option is used for I<-encap>/I<-decap> commands and specifies the KEM
-operation specific for the key algorithm when there is no default KEM
-operation.
-If the algorithm has the default KEM operation, this option can be omitted.
-
-See L<EVP_PKEY_CTX_set_kem_op(3)> and algorithm-specific KEM documentation e.g.
-L<EVP_KEM-RSA(7)>, L<EVP_KEM-EC(7)>, L<EVP_KEM-X25519(7)>, and
-L<EVP_KEM-X448(7)>.
+This option is used with the I<-encap>/I<-decap> commands and specifies the KEM
+operation (mode) specific for the key algorithm when there is no default way
+to encapsulate and decapsulate shared secrets with the chosen key type.
+This is needed only for RSA, where B<RSASVE>, is not the default mode, even
+though it is presently the only RSA KEM supported.
+See L<EVP_PKEY_CTX_set_kem_op(3)> and L<EVP_KEM-RSA(7)>.
=item B<-kdf> I<algorithm>
setup("test_pkeyutl");
-plan tests => 25;
+plan tests => 27;
# For the tests below we use the cert itself as the TBS file
# openssl pkeyutl -decap -inkey rsa_priv.pem -in encap_out.bin -out decap_out.bin
# decap_out is equal to secret
SKIP: {
- skip "RSA is not supported by this OpenSSL build", 5
- if disabled("rsa");
+ skip "RSA is not supported by this OpenSSL build", 7
+ if disabled("rsa"); # Note "rsa" isn't (yet?) disablable.
# Self-compat
- ok(run(app(([ 'openssl', 'pkeyutl', '-encap', '-pubin', '-kemop', 'RSASVE',
+ ok(run(app(([ 'openssl', 'pkeyutl', '-encap', '-kemop', 'RSASVE',
'-inkey', srctop_file('test', 'testrsa2048pub.pem'),
'-out', 'encap_out.bin', '-secret', 'secret.bin']))),
"RSA pubkey encapsulation");
+ ok(run(app(([ 'openssl', 'pkeyutl', '-decap', '-kemop', 'RSASVE',
+ '-inkey', srctop_file('test', 'testrsa2048.pem'),
+ '-in', 'encap_out.bin', '-secret', 'decap_secret.bin']))),
+ "RSA pubkey decapsulation");
+ is(compare("secret.bin", "decap_secret.bin"), 0, "Secret is correctly decapsulated");
+
+ # Legacy CLI with decap output written to '-out'
ok(run(app(([ 'openssl', 'pkeyutl', '-decap', '-kemop', 'RSASVE',
'-inkey', srctop_file('test', 'testrsa2048.pem'),
'-in', 'encap_out.bin', '-out', 'decap_out.bin']))),
# Pregenerated
ok(run(app(([ 'openssl', 'pkeyutl', '-decap', '-kemop', 'RSASVE',
'-inkey', srctop_file('test', 'testrsa2048.pem'),
- '-in', srctop_file('test', 'encap_out.bin'), '-out', 'decap_out_etl.bin']))),
+ '-in', srctop_file('test', 'encap_out.bin'),
+ '-secret', 'decap_out_etl.bin']))),
"RSA pubkey decapsulation - pregenerated");
is(compare(srctop_file('test', 'encap_secret.bin'), "decap_out_etl.bin"), 0,
"Secret is correctly decapsulated - pregenerated");
}
-