001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2021, Connect2id Ltd and contributors.
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.JOSEException;
022import com.nimbusds.jose.JWECryptoParts;
023import com.nimbusds.jose.JWEEncrypter;
024import com.nimbusds.jose.JWEHeader;
025import com.nimbusds.jose.crypto.impl.ECDH;
026import com.nimbusds.jose.crypto.impl.ECDH1PU;
027import com.nimbusds.jose.crypto.impl.ECDH1PUCryptoProvider;
028import com.nimbusds.jose.jwk.Curve;
029import com.nimbusds.jose.jwk.OctetKeyPair;
030import com.nimbusds.jose.jwk.gen.OctetKeyPairGenerator;
031import net.jcip.annotations.ThreadSafe;
032
033import javax.crypto.SecretKey;
034import java.util.Collections;
035import java.util.Set;
036
037
038/**
039 * Elliptic Curve Diffie-Hellman encrypter of
040 * {@link com.nimbusds.jose.JWEObject JWE objects} for curves using an OKP JWK.
041 * Expects a public {@link OctetKeyPair} key with {@code "crv"} X25519.
042 *
043 * <p>See <a href="https://tools.ietf.org/html/rfc8037">RFC 8037</a>
044 * for more information.
045 *
046 * <p>See also {@link ECDH1PUEncrypter} for ECDH on other curves.
047 *
048 * <p>Public Key Authenticated Encryption for JOSE
049 * <a href="https://datatracker.ietf.org/doc/html/draft-madden-jose-ecdh-1pu-04">ECDH-1PU</a>
050 * for more information.
051 *
052 * <p>This class is thread-safe.
053 *
054 * <p>Supports the following key management algorithms:
055 *
056 * <ul>
057 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_1PU}
058 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_1PU_A128KW}
059 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_1PU_A192KW}
060 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_1PU_A256KW}
061 * </ul>
062 *
063 * <p>Supports the following elliptic curves:
064 *
065 * <ul>
066 *     <li>{@link Curve#X25519}
067 * </ul>
068 *
069 * <p>Supports the following content encryption algorithms for Direct key
070 * agreement mode:
071 *
072 * <ul>
073 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256}
074 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384}
075 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512}
076 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM}
077 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM}
078 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM}
079 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED}
080 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED}
081 *     <li>{@link com.nimbusds.jose.EncryptionMethod#XC20P}
082 * </ul>
083 *
084 * <p>Supports the following content encryption algorithms for Key wrapping
085 * mode:
086 *
087 * <ul>
088 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256}
089 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384}
090 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512}
091 * </ul>
092 *
093 * @author Alexander Martynov
094 * @version 2021-08-03
095 */
096@ThreadSafe
097public class ECDH1PUX25519Encrypter extends ECDH1PUCryptoProvider implements JWEEncrypter {
098
099
100    /**
101     * The public key.
102     */
103    private final OctetKeyPair publicKey;
104
105    /**
106     * The private key.
107     */
108    private final OctetKeyPair privateKey;
109
110    /**
111     * The externally supplied AES content encryption key (CEK) to use,
112     * {@code null} to generate a CEK for each JWE.
113     */
114    private final SecretKey contentEncryptionKey;
115
116    /**
117     * Creates a new Curve25519 Elliptic Curve Diffie-Hellman encrypter.
118     *
119     * @param privateKey The private key. Must not be {@code null}.
120     * @param publicKey The public key. Must not be {@code null}.
121     *
122     * @throws JOSEException If the key subtype is not supported.
123     */
124    public ECDH1PUX25519Encrypter(final OctetKeyPair privateKey, final OctetKeyPair publicKey)
125            throws JOSEException {
126
127        this(privateKey, publicKey, null);
128    }
129
130    /**
131     * Creates a new Curve25519 Elliptic Curve Diffie-Hellman encrypter.
132     *
133     * @param privateKey The private key. Must not be {@code null}.
134     * @param publicKey The public key. Must not be {@code null}.
135     * @param contentEncryptionKey The content encryption key (CEK) to use.
136     *                             If specified its algorithm must be "AES"
137     *                             and its length must match the expected
138     *                             for the JWE encryption method ("enc").
139     *                             If {@code null} a CEK will be generated
140     *                             for each JWE.
141     *
142     * @throws JOSEException If the key subtype is not supported.
143     */
144    public ECDH1PUX25519Encrypter(final OctetKeyPair privateKey,
145                                  final OctetKeyPair publicKey,
146                                  final SecretKey contentEncryptionKey
147                                  )
148            throws JOSEException {
149
150        super(publicKey.getCurve());
151
152        this.publicKey = publicKey;
153        this.privateKey = privateKey;
154
155        if (contentEncryptionKey != null && (contentEncryptionKey.getAlgorithm() == null || !contentEncryptionKey.getAlgorithm().equals("AES")))
156            throw new IllegalArgumentException("The algorithm of the content encryption key (CEK) must be AES");
157
158        this.contentEncryptionKey = contentEncryptionKey;
159    }
160
161    @Override
162    public Set<Curve> supportedEllipticCurves() {
163
164        return Collections.singleton(Curve.X25519);
165    }
166
167
168    /**
169     * Returns the public key.
170     *
171     * @return The public key.
172     */
173    public OctetKeyPair getPublicKey() {
174
175        return publicKey;
176    }
177
178    /**
179     * Returns the private key.
180     *
181     * @return The private key.
182     */
183    public OctetKeyPair getPrivateKey() {
184
185        return privateKey;
186    }
187
188    @Override
189    public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText)
190            throws JOSEException {
191
192        ECDH1PU.validateSameCurve(privateKey, publicKey);
193
194        final OctetKeyPair ephemeralPrivateKey = new OctetKeyPairGenerator(getCurve()).generate();
195        final OctetKeyPair ephemeralPublicKey = ephemeralPrivateKey.toPublicJWK();
196
197        // Add the ephemeral public EC key to the header
198        JWEHeader updatedHeader = new JWEHeader.Builder(header).
199                ephemeralPublicKey(ephemeralPublicKey).
200                build();
201
202        SecretKey Ze = ECDH.deriveSharedSecret(
203                publicKey,
204                ephemeralPrivateKey);
205
206        SecretKey Zs = ECDH.deriveSharedSecret(
207                publicKey,
208                privateKey);
209
210        SecretKey Z = ECDH1PU.deriveZ(Ze, Zs);
211
212        return encryptWithZ(updatedHeader, Z, clearText, contentEncryptionKey);
213    }
214}