001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2026, Connect2id Ltd.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.jose.crypto;
019
020
021import com.nimbusds.jose.*;
022import com.nimbusds.jose.crypto.impl.AAD;
023import com.nimbusds.jose.crypto.impl.CriticalHeaderParamsDeferral;
024import com.nimbusds.jose.crypto.impl.ECDH;
025import com.nimbusds.jose.crypto.impl.ECDHCryptoProvider;
026import com.nimbusds.jose.crypto.opts.OptionUtils;
027import com.nimbusds.jose.jwk.Curve;
028import com.nimbusds.jose.jwk.OctetKeyPair;
029import com.nimbusds.jose.util.Base64URL;
030
031import javax.crypto.SecretKey;
032import java.util.Collections;
033import java.util.Set;
034
035
036/**
037 * Curve25519 Elliptic Curve Diffie-Hellman decrypter of
038 * {@link com.nimbusds.jose.JWEObject JWE objects}.
039 * Expects a private {@link OctetKeyPair} key with {@code "crv"} X25519.
040 *
041 * <p>See <a href="https://tools.ietf.org/html/rfc8037">RFC 8037</a>
042 * for more information.
043 *
044 * <p>See also {@link ECDHDecrypter} for ECDH on other curves.
045 *
046 * <p>This class is thread-safe.
047 *
048 * <p>Supports the following key management algorithms:
049 *
050 * <ul>
051 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES}
052 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A128KW}
053 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A192KW}
054 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A256KW}
055 * </ul>
056 *
057 * <p>Supports the following elliptic curve:
058 *
059 * <ul>
060 *     <li>{@link com.nimbusds.jose.jwk.Curve#X25519} (Curve25519)
061 * </ul>
062 *
063 * <p>Supports the following content encryption algorithms:
064 *
065 * <ul>
066 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256}
067 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384}
068 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512}
069 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM}
070 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM}
071 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM}
072 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED}
073 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED}
074 *     <li>{@link com.nimbusds.jose.EncryptionMethod#XC20P}
075 * </ul>
076 *
077 * @author Tim McLean
078 * @author Egor Puzanov
079 * @author Vladimir Dzhuvinov
080 * @version 2026-09-16
081 */
082public class X25519Decrypter extends ECDHCryptoProvider implements JWEDecrypter, CriticalHeaderParamsAware {
083
084
085        /**
086         * The private key.
087         */
088        private final OctetKeyPair privateKey;
089
090
091        /**
092         * The critical header policy.
093         */
094        private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral();
095
096
097        /**
098         * The configured options, empty set if none.
099         */
100        private final Set<JWEDecrypterOption> opts;
101
102
103        /**
104         * Creates a new Curve25519 Elliptic Curve Diffie-Hellman decrypter.
105         *
106         * @param privateKey The private key. Must not be {@code null}.
107         *
108         * @throws JOSEException If the key subtype is not supported.
109         */
110        public X25519Decrypter(final OctetKeyPair privateKey)
111                throws JOSEException {
112
113                this(privateKey, null, Collections.<JWEDecrypterOption>emptySet());
114        }
115
116
117        /**
118         * Creates a new Curve25519 Elliptic Curve Diffie-Hellman decrypter.
119         *
120         * @param privateKey     The private key. Must not be {@code null}.
121         * @param defCritHeaders The names of the critical header parameters
122         *                       that are deferred to the application for
123         *                       processing, empty set or {@code null} if none.
124         *
125         * @throws JOSEException If the key subtype is not supported.
126         */
127        public X25519Decrypter(final OctetKeyPair privateKey, final Set<String> defCritHeaders)
128                throws JOSEException {
129
130                this(privateKey, defCritHeaders, Collections.<JWEDecrypterOption>emptySet());
131        }
132
133
134        /**
135         * Creates a new Curve25519 Elliptic Curve Diffie-Hellman decrypter.
136         *
137         * @param privateKey     The private key. Must not be {@code null}.
138         * @param defCritHeaders The names of the critical header parameters
139         *                       that are deferred to the application for
140         *                       processing, empty set or {@code null} if none.
141         * @param opts           The decryption options, empty or {@code null}
142         *                       if none.
143         *
144         * @throws JOSEException If the key subtype is not supported.
145         */
146        public X25519Decrypter(final OctetKeyPair privateKey,
147                               final Set<String> defCritHeaders,
148                               final Set<JWEDecrypterOption> opts)
149                throws JOSEException {
150
151                super(privateKey.getCurve(), null);
152
153                if (! Curve.X25519.equals(privateKey.getCurve())) {
154                        throw new JOSEException("X25519Decrypter only supports OctetKeyPairs with crv=X25519");
155                }
156
157                if (! privateKey.isPrivate()) {
158                        throw new JOSEException("The OctetKeyPair doesn't contain a private part");
159                }
160
161                this.privateKey = privateKey;
162
163                this.opts = opts != null ? opts : Collections.<JWEDecrypterOption>emptySet();
164
165                critPolicy.setDeferredCriticalHeaderParams(defCritHeaders);
166        }
167
168
169        @Override
170        public Set<Curve> supportedEllipticCurves() {
171
172                return Collections.singleton(Curve.X25519);
173        }
174
175
176        /**
177         * Returns the private key.
178         *
179         * @return The private key.
180         */
181        public OctetKeyPair getPrivateKey() {
182
183                return privateKey;
184        }
185
186
187        @Override
188        public Set<String> getProcessedCriticalHeaderParams() {
189
190                return critPolicy.getProcessedCriticalHeaderParams();
191        }
192
193
194        @Override
195        public Set<String> getDeferredCriticalHeaderParams() {
196
197                return critPolicy.getDeferredCriticalHeaderParams();
198        }
199
200
201        /**
202         * Decrypts the specified cipher text of a {@link JWEObject JWE Object}.
203         *
204         * @param header       The JSON Web Encryption (JWE) header. Must
205         *                     specify a supported JWE algorithm and method.
206         *                     Must not be {@code null}.
207         * @param encryptedKey The encrypted key, {@code null} if not required
208         *                     by the JWE algorithm.
209         * @param iv           The initialisation vector, {@code null} if not
210         *                     required by the JWE algorithm.
211         * @param cipherText   The cipher text to decrypt. Must not be
212         *                     {@code null}.
213         * @param authTag      The authentication tag, {@code null} if not
214         *                     required.
215         *
216         * @return The clear text.
217         *
218         * @throws JOSEException If the JWE algorithm or method is not
219         *                       supported, if a critical header parameter is
220         *                       not supported or marked for deferral to the
221         *                       application, or if decryption failed for some
222         *                       other reason.
223         */
224        @Deprecated
225        public byte[] decrypt(final JWEHeader header,
226                       final Base64URL encryptedKey,
227                       final Base64URL iv,
228                       final Base64URL cipherText,
229                       final Base64URL authTag)
230                throws JOSEException {
231
232                return decrypt(header, encryptedKey, iv, cipherText, authTag, AAD.compute(header));
233        }
234
235
236        @Override
237        public byte[] decrypt(final JWEHeader header,
238                              final Base64URL encryptedKey,
239                              final Base64URL iv,
240                              final Base64URL cipherText,
241                              final Base64URL authTag,
242                              final byte[] aad)
243                throws JOSEException {
244
245                // Check for unrecognizable "crit" properties
246                critPolicy.ensureHeaderPasses(header);
247
248                // Get ephemeral key from header
249                OctetKeyPair ephemeralPublicKey = (OctetKeyPair) header.getEphemeralPublicKey();
250
251                if (ephemeralPublicKey == null) {
252                        throw new JOSEException("Missing ephemeral public key epk JWE header parameter");
253                }
254
255                if (! privateKey.getCurve().equals(ephemeralPublicKey.getCurve())) {
256                        throw new JOSEException("Curve of ephemeral public key does not match curve of private key");
257                }
258
259                // Derive 'Z'
260                // Note: X25519 does not require public key validation
261                // See https://cr.yp.to/ecdh.html#validate
262                SecretKey Z = ECDH.deriveSharedSecret(ephemeralPublicKey, privateKey);
263
264                int maxDecompressedLength = OptionUtils.resolveMaxDecompressedPlainTextLength(opts);
265
266                return decryptWithZ(header, aad, Z, encryptedKey, iv, cipherText, authTag, maxDecompressedLength);
267        }
268}