001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2016, 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 java.security.*;
022import java.util.Collections;
023import java.util.Set;
024import javax.crypto.SecretKey;
025
026import com.google.crypto.tink.subtle.X25519;
027import com.nimbusds.jose.*;
028import com.nimbusds.jose.crypto.impl.ECDH;
029import com.nimbusds.jose.crypto.impl.ECDHCryptoProvider;
030import com.nimbusds.jose.jwk.Curve;
031import com.nimbusds.jose.jwk.OctetKeyPair;
032import com.nimbusds.jose.util.Base64URL;
033import net.jcip.annotations.ThreadSafe;
034
035
036/**
037 * Curve25519 Elliptic Curve Diffie-Hellman encrypter of
038 * {@link com.nimbusds.jose.JWEObject JWE objects}.
039 * Expects a public {@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 ECDHEncrypter} 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 * @version 2018-07-12
079 */
080@ThreadSafe
081public class X25519Encrypter extends ECDHCryptoProvider implements JWEEncrypter {
082
083
084        /**
085         * The public key.
086         */
087        private final OctetKeyPair publicKey;
088
089
090        /**
091         * Creates a new Curve25519 Elliptic Curve Diffie-Hellman encrypter.
092         *
093         * @param publicKey The public key. Must not be {@code null}.
094         *
095         * @throws JOSEException If the key subtype is not supported.
096         */
097        public X25519Encrypter(final OctetKeyPair publicKey)
098                throws JOSEException {
099
100                super(publicKey.getCurve());
101
102                if (! Curve.X25519.equals(publicKey.getCurve())) {
103                        throw new JOSEException("X25519Encrypter only supports OctetKeyPairs with crv=X25519");
104                }
105
106                if (publicKey.isPrivate()) {
107                        throw new JOSEException("X25519Encrypter requires a public key, use OctetKeyPair.toPublicJWK()");
108                }
109
110                this.publicKey = publicKey;
111        }
112
113
114        @Override
115        public Set<Curve> supportedEllipticCurves() {
116
117                return Collections.singleton(Curve.X25519);
118        }
119
120
121        /**
122         * Returns the public key.
123         *
124         * @return The public key.
125         */
126        public OctetKeyPair getPublicKey() {
127
128                return publicKey;
129        }
130
131
132        @Override
133        public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText)
134                throws JOSEException {
135
136                // Generate ephemeral X25519 key pair
137                final byte[] ephemeralPrivateKeyBytes = X25519.generatePrivateKey();
138                final byte[] ephemeralPublicKeyBytes;
139                try {
140                        ephemeralPublicKeyBytes = X25519.publicFromPrivate(ephemeralPrivateKeyBytes);
141
142                } catch (InvalidKeyException e) {
143                        // Should never happen since we just generated this private key
144                        throw new JOSEException(e.getMessage(), e);
145                }
146
147                final OctetKeyPair ephemeralPrivateKey =
148                        new OctetKeyPair.Builder(getCurve(), Base64URL.encode(ephemeralPublicKeyBytes)).
149                        d(Base64URL.encode(ephemeralPrivateKeyBytes)).
150                        build();
151                final OctetKeyPair ephemeralPublicKey = ephemeralPrivateKey.toPublicJWK();
152
153                // Add the ephemeral public EC key to the header
154                JWEHeader updatedHeader = new JWEHeader.Builder(header).
155                        ephemeralPublicKey(ephemeralPublicKey).
156                        build();
157
158                // Derive 'Z'
159                SecretKey Z = ECDH.deriveSharedSecret(publicKey, ephemeralPrivateKey);
160
161                return encryptWithZ(updatedHeader, Z, clearText);
162        }
163}