001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2018, 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.util.Collections;
022import java.util.Set;
023import javax.crypto.SecretKey;
024
025import com.nimbusds.jose.*;
026import com.nimbusds.jose.crypto.impl.CriticalHeaderParamsDeferral;
027import com.nimbusds.jose.crypto.impl.ECDH;
028import com.nimbusds.jose.crypto.impl.ECDHCryptoProvider;
029import com.nimbusds.jose.jwk.Curve;
030import com.nimbusds.jose.jwk.OctetKeyPair;
031import com.nimbusds.jose.util.Base64URL;
032
033
034/**
035 * Curve25519 Elliptic Curve Diffie-Hellman decrypter of
036 * {@link com.nimbusds.jose.JWEObject JWE objects}.
037 * Expects a private {@link OctetKeyPair} key with {@code "crv"} X25519.
038 *
039 * <p>See <a href="https://tools.ietf.org/html/rfc8037">RFC 8037</a>
040 * for more information.
041 *
042 * <p>See also {@link ECDHDecrypter} for ECDH on other curves.
043 *
044 * <p>This class is thread-safe.
045 *
046 * <p>Supports the following key management algorithms:
047 *
048 * <ul>
049 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES}
050 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A128KW}
051 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A192KW}
052 *     <li>{@link com.nimbusds.jose.JWEAlgorithm#ECDH_ES_A256KW}
053 * </ul>
054 *
055 * <p>Supports the following elliptic curve:
056 *
057 * <ul>
058 *     <li>{@link com.nimbusds.jose.jwk.Curve#X25519} (Curve25519)
059 * </ul>
060 *
061 * <p>Supports the following content encryption algorithms:
062 *
063 * <ul>
064 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256}
065 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384}
066 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512}
067 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM}
068 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM}
069 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM}
070 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256_DEPRECATED}
071 *     <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512_DEPRECATED}
072 * </ul>
073 *
074 * @author Tim McLean
075 * @version 2018-07-12
076 */
077public class X25519Decrypter extends ECDHCryptoProvider implements JWEDecrypter, CriticalHeaderParamsAware {
078
079
080        /**
081         * The private key.
082         */
083        private final OctetKeyPair privateKey;
084
085
086        /**
087         * The critical header policy.
088         */
089        private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral();
090
091
092        /**
093         * Creates a new Curve25519 Elliptic Curve Diffie-Hellman decrypter.
094         *
095         * @param privateKey The private key. Must not be {@code null}.
096         *
097         * @throws JOSEException If the key subtype is not supported.
098         */
099        public X25519Decrypter(final OctetKeyPair privateKey)
100                throws JOSEException {
101
102                this(privateKey, null);
103        }
104
105
106        /**
107         * Creates a new Curve25519 Elliptic Curve Diffie-Hellman decrypter.
108         *
109         * @param privateKey     The private key. Must not be {@code null}.
110         * @param defCritHeaders The names of the critical header parameters
111         *                       that are deferred to the application for
112         *                       processing, empty set or {@code null} if none.
113         *
114         * @throws JOSEException If the key subtype is not supported.
115         */
116        public X25519Decrypter(final OctetKeyPair privateKey, final Set<String> defCritHeaders)
117                throws JOSEException {
118
119                super(privateKey.getCurve());
120
121                if (! Curve.X25519.equals(privateKey.getCurve())) {
122                        throw new JOSEException("X25519Decrypter only supports OctetKeyPairs with crv=X25519");
123                }
124
125                if (! privateKey.isPrivate()) {
126                        throw new JOSEException("The OctetKeyPair doesn't contain a private part");
127                }
128
129                this.privateKey = privateKey;
130
131                critPolicy.setDeferredCriticalHeaderParams(defCritHeaders);
132        }
133
134
135        @Override
136        public Set<Curve> supportedEllipticCurves() {
137
138                return Collections.singleton(Curve.X25519);
139        }
140
141
142        /**
143         * Returns the private key.
144         *
145         * @return The private key.
146         */
147        public OctetKeyPair getPrivateKey() {
148
149                return privateKey;
150        }
151
152
153        @Override
154        public Set<String> getProcessedCriticalHeaderParams() {
155
156                return critPolicy.getProcessedCriticalHeaderParams();
157        }
158
159
160        @Override
161        public Set<String> getDeferredCriticalHeaderParams() {
162
163                return critPolicy.getProcessedCriticalHeaderParams();
164        }
165
166
167        @Override
168        public byte[] decrypt(final JWEHeader header,
169                              final Base64URL encryptedKey,
170                              final Base64URL iv,
171                              final Base64URL cipherText,
172                              final Base64URL authTag)
173                throws JOSEException {
174
175                // Check for unrecognizable "crit" properties
176                critPolicy.ensureHeaderPasses(header);
177
178                // Get ephemeral key from header
179                OctetKeyPair ephemeralPublicKey = (OctetKeyPair) header.getEphemeralPublicKey();
180
181                if (ephemeralPublicKey == null) {
182                        throw new JOSEException("Missing ephemeral public key \"epk\" JWE header parameter");
183                }
184
185                if (! privateKey.getCurve().equals(ephemeralPublicKey.getCurve())) {
186                        throw new JOSEException("Curve of ephemeral public key does not match curve of private key");
187                }
188
189                // Derive 'Z'
190                // Note: X25519 does not require public key validation
191                // See https://cr.yp.to/ecdh.html#validate
192                SecretKey Z = ECDH.deriveSharedSecret(ephemeralPublicKey, privateKey);
193
194                return decryptWithZ(header, Z, encryptedKey, iv, cipherText, authTag);
195        }
196}