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