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 * </ul>
075 *
076 * @author Tim McLean
077 * @version 2018-07-12
078 */
079@ThreadSafe
080public class X25519Encrypter extends ECDHCryptoProvider implements JWEEncrypter {
081
082
083        /**
084         * The public key.
085         */
086        private final OctetKeyPair publicKey;
087
088
089        /**
090         * Creates a new Curve25519 Elliptic Curve Diffie-Hellman encrypter.
091         *
092         * @param publicKey The public key. Must not be {@code null}.
093         *
094         * @throws JOSEException If the key subtype is not supported.
095         */
096        public X25519Encrypter(final OctetKeyPair publicKey)
097                throws JOSEException {
098
099                super(publicKey.getCurve());
100
101                if (! Curve.X25519.equals(publicKey.getCurve())) {
102                        throw new JOSEException("X25519Encrypter only supports OctetKeyPairs with crv=X25519");
103                }
104
105                if (publicKey.isPrivate()) {
106                        throw new JOSEException("X25519Encrypter requires a public key, use OctetKeyPair.toPublicJWK()");
107                }
108
109                this.publicKey = publicKey;
110        }
111
112
113        @Override
114        public Set<Curve> supportedEllipticCurves() {
115
116                return Collections.singleton(Curve.X25519);
117        }
118
119
120        /**
121         * Returns the public key.
122         *
123         * @return The public key.
124         */
125        public OctetKeyPair getPublicKey() {
126
127                return publicKey;
128        }
129
130
131        @Override
132        public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText)
133                throws JOSEException {
134
135                // Generate ephemeral X25519 key pair
136                final byte[] ephemeralPrivateKeyBytes = X25519.generatePrivateKey();
137                final byte[] ephemeralPublicKeyBytes;
138                try {
139                        ephemeralPublicKeyBytes = X25519.publicFromPrivate(ephemeralPrivateKeyBytes);
140
141                } catch (InvalidKeyException e) {
142                        // Should never happen since we just generated this private key
143                        throw new JOSEException(e.getMessage(), e);
144                }
145
146                final OctetKeyPair ephemeralPrivateKey =
147                        new OctetKeyPair.Builder(getCurve(), Base64URL.encode(ephemeralPublicKeyBytes)).
148                        d(Base64URL.encode(ephemeralPrivateKeyBytes)).
149                        build();
150                final OctetKeyPair ephemeralPublicKey = ephemeralPrivateKey.toPublicJWK();
151
152                // Add the ephemeral public EC key to the header
153                JWEHeader updatedHeader = new JWEHeader.Builder(header).
154                        ephemeralPublicKey(ephemeralPublicKey).
155                        build();
156
157                // Derive 'Z'
158                SecretKey Z = ECDH.deriveSharedSecret(publicKey, ephemeralPrivateKey);
159
160                return encryptWithZ(updatedHeader, Z, clearText);
161        }
162}