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