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 com.google.crypto.tink.subtle.Ed25519Verify;
022import com.nimbusds.jose.*;
023import com.nimbusds.jose.crypto.impl.CriticalHeaderParamsDeferral;
024import com.nimbusds.jose.crypto.impl.EdDSAProvider;
025import com.nimbusds.jose.jwk.Curve;
026import com.nimbusds.jose.jwk.OctetKeyPair;
027import com.nimbusds.jose.util.Base64URL;
028import net.jcip.annotations.ThreadSafe;
029
030import java.security.GeneralSecurityException;
031import java.util.Set;
032
033
034/**
035 * Ed25519 verifier of {@link com.nimbusds.jose.JWSObject JWS objects}.
036 * Expects a public {@link OctetKeyPair} with {@code "crv"} Ed25519.
037 * Uses the Edwards-curve Digital Signature Algorithm (EdDSA).
038 *
039 * <p>See <a href="https://tools.ietf.org/html/rfc8037">RFC 8037</a>
040 * for more information.
041 *
042 * <p>This class is thread-safe.
043 *
044 * <p>Supports the following algorithm:
045 *
046 * <ul>
047 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#EdDSA}
048 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#EdDSA} with
049 *         {@link com.nimbusds.jose.jwk.Curve#Ed25519}
050 * </ul>
051 *
052 * @author Tim McLean
053 * @version 2024-05-07
054 */
055@ThreadSafe
056public class Ed25519Verifier extends EdDSAProvider implements JWSVerifier, CriticalHeaderParamsAware {
057
058
059        private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral();
060
061
062        private final OctetKeyPair publicKey;
063
064
065        private final Ed25519Verify tinkVerifier;
066
067
068        /**
069         * Creates a new Ed25519 verifier.
070         *
071         * @param publicKey The public Ed25519 key. Must not be {@code null}.
072         *
073         * @throws JOSEException If the key subtype is not supported
074         */
075        public Ed25519Verifier(final OctetKeyPair publicKey)
076                throws JOSEException {
077
078                this(publicKey, null);
079        }
080
081
082        /**
083         * Creates an Ed25519 verifier.
084         *
085         * @param publicKey      The public Ed25519 key. Must not be
086         *                       {@code null}.
087         * @param defCritHeaders The names of the critical header parameters
088         *                       that are deferred to the application for
089         *                       processing, empty set or {@code null} if none.
090         *
091         * @throws JOSEException If the key subtype is not supported.
092         */
093        public Ed25519Verifier(final OctetKeyPair publicKey, final Set<String> defCritHeaders)
094                throws JOSEException {
095
096                super();
097
098                if (! Curve.Ed25519.equals(publicKey.getCurve())) {
099                        throw new JOSEException("Ed25519Verifier only supports OctetKeyPairs with crv=Ed25519");
100                }
101
102                if (publicKey.isPrivate()) {
103                        throw new JOSEException("Ed25519Verifier requires a public key, use OctetKeyPair.toPublicJWK()");
104                }
105
106                this.publicKey = publicKey;
107                tinkVerifier = new Ed25519Verify(publicKey.getDecodedX());
108                critPolicy.setDeferredCriticalHeaderParams(defCritHeaders);
109        }
110
111
112        /**
113         * Returns the public key.
114         *
115         * @return An OctetKeyPair without the private part
116         */
117        public OctetKeyPair getPublicKey() {
118
119                return publicKey;
120        }
121
122
123        @Override
124        public Set<String> getProcessedCriticalHeaderParams() {
125
126                return critPolicy.getProcessedCriticalHeaderParams();
127        }
128
129
130        @Override
131        public Set<String> getDeferredCriticalHeaderParams() {
132
133                return critPolicy.getProcessedCriticalHeaderParams();
134        }
135
136
137        @Override
138        public boolean verify(final JWSHeader header,
139                              final byte[] signedContent, 
140                              final Base64URL signature)
141                throws JOSEException {
142
143                // Check alg field in header
144                final JWSAlgorithm alg = header.getAlgorithm();
145                if (! JWSAlgorithm.Ed25519.equals(alg) && ! JWSAlgorithm.EdDSA.equals(alg)) {
146                        throw new JOSEException("Ed25519Verifier requires alg=Ed25519 or alg=EdDSA in JWSHeader");
147                }
148
149                // Check for unrecognized "crit" properties
150                if (! critPolicy.headerPasses(header)) {
151                        return false;
152                }
153
154                final byte[] jwsSignature = signature.decode();
155
156                try {
157                        tinkVerifier.verify(jwsSignature, signedContent);
158                        return true;
159
160                } catch (GeneralSecurityException e) {
161                        return false;
162                }
163        }
164}