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 com.nimbusds.jose.CriticalHeaderParamsAware;
022import com.nimbusds.jose.JOSEException;
023import com.nimbusds.jose.JWSHeader;
024import com.nimbusds.jose.JWSVerifier;
025import com.nimbusds.jose.crypto.impl.CriticalHeaderParamsDeferral;
026import com.nimbusds.jose.crypto.impl.HMAC;
027import com.nimbusds.jose.crypto.impl.MACProvider;
028import com.nimbusds.jose.crypto.utils.ConstantTimeUtils;
029import com.nimbusds.jose.jwk.OctetSequenceKey;
030import com.nimbusds.jose.util.Base64URL;
031import com.nimbusds.jose.util.ByteUtils;
032import com.nimbusds.jose.util.StandardCharset;
033import net.jcip.annotations.ThreadSafe;
034
035import javax.crypto.SecretKey;
036import java.util.Set;
037
038
039/**
040 * Message Authentication Code (MAC) verifier of 
041 * {@link com.nimbusds.jose.JWSObject JWS objects}. Expects a secret key.
042 *
043 * <p>See RFC 7518
044 * <a href="https://tools.ietf.org/html/rfc7518#section-3.2">section 3.2</a>
045 * for more information.
046 *
047 * <p>This class is thread-safe.
048 *
049 * <p>Supports the following algorithms:
050 *
051 * <ul>
052 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#HS256}
053 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#HS384}
054 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#HS512}
055 * </ul>
056 *
057 <p>Tested with the AWS CloudHSM JCE provider.
058 * 
059 * @author Vladimir Dzhuvinov
060 * @version 2024-10-28
061 */
062@ThreadSafe
063public class MACVerifier extends MACProvider implements JWSVerifier, CriticalHeaderParamsAware {
064
065
066        /**
067         * The critical header policy.
068         */
069        private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral();
070
071
072        /**
073         * Creates a new Message Authentication (MAC) verifier.
074         *
075         * @param secret The secret. Must be at least 256 bits long and not
076         *               {@code null}.
077         *
078         * @throws JOSEException If the secret length is shorter than the
079         *                       minimum 256-bit requirement.
080         */
081        public MACVerifier(final byte[] secret)
082                throws JOSEException {
083
084                super(secret);
085        }
086
087
088        /**
089         * Creates a new Message Authentication (MAC) verifier.
090         *
091         * @param secretString The secret as a UTF-8 encoded string. Must be at
092         *                     least 256 bits long and not {@code null}.
093         *
094         * @throws JOSEException If the secret length is shorter than the
095         *                       minimum 256-bit requirement.
096         */
097        public MACVerifier(final String secretString)
098                throws JOSEException {
099
100                this(secretString.getBytes(StandardCharset.UTF_8));
101        }
102
103
104        /**
105         * Creates a new Message Authentication (MAC) verifier.
106         *
107         * @param secretKey The secret key. Must be at least 256 bits long and
108         *                  not {@code null}.
109         *
110         * @throws JOSEException If the secret length is shorter than the
111         *                       minimum 256-bit requirement.
112         */
113        public MACVerifier(final SecretKey secretKey)
114                throws JOSEException {
115
116                this(secretKey, null);
117        }
118
119
120        /**
121         * Creates a new Message Authentication (MAC) verifier.
122         *
123         * @param jwk The secret as a JWK. Must be at least 256 bits long and
124         *            not {@code null}.
125         *
126         * @throws JOSEException If the secret length is shorter than the
127         *                       minimum 256-bit requirement.
128         */
129        public MACVerifier(final OctetSequenceKey jwk)
130                throws JOSEException {
131
132                this(jwk.toByteArray());
133        }
134
135
136        /**
137         * Creates a new Message Authentication (MAC) verifier.
138         *
139         * @param secret         The secret. Must be at least 256 bits long
140         *                       and not {@code null}.
141         * @param defCritHeaders The names of the critical header parameters
142         *                       that are deferred to the application for
143         *                       processing, empty set or {@code null} if none.
144         *
145         * @throws JOSEException If the secret length is shorter than the
146         *                       minimum 256-bit requirement.
147         */
148        public MACVerifier(final byte[] secret, final Set<String> defCritHeaders)
149                throws JOSEException {
150
151                super(secret);
152
153                critPolicy.setDeferredCriticalHeaderParams(defCritHeaders);
154        }
155
156
157        /**
158         * Creates a new Message Authentication (MAC) verifier.
159         *
160         * @param secretKey      The secret key. Must be at least 256 bits long
161         *                       and not {@code null}.
162         * @param defCritHeaders The names of the critical header parameters
163         *                       that are deferred to the application for
164         *                       processing, empty set or {@code null} if none.
165         *
166         * @throws JOSEException If the secret length is shorter than the
167         *                       minimum 256-bit requirement.
168         */
169        public MACVerifier(final SecretKey secretKey,
170                           final Set<String> defCritHeaders)
171                throws JOSEException {
172
173                super(secretKey);
174
175                critPolicy.setDeferredCriticalHeaderParams(defCritHeaders);
176        }
177
178
179        /**
180         * Creates a new Message Authentication (MAC) verifier.
181         *
182         * @param jwk            The secret as a JWK. Must be at least 256 bits
183         *                       long and not {@code null}.
184         * @param defCritHeaders The names of the critical header parameters
185         *                       that are deferred to the application for
186         *                       processing, empty set or {@code null} if none.
187         *
188         * @throws JOSEException If the secret length is shorter than the
189         *                       minimum 256-bit requirement.
190         */
191        public MACVerifier(final OctetSequenceKey jwk,
192                           final Set<String> defCritHeaders)
193                throws JOSEException {
194
195                this(jwk.toByteArray(), defCritHeaders);
196        }
197
198
199        @Override
200        public Set<String> getProcessedCriticalHeaderParams() {
201
202                return critPolicy.getProcessedCriticalHeaderParams();
203        }
204
205
206        @Override
207        public Set<String> getDeferredCriticalHeaderParams() {
208
209                return critPolicy.getProcessedCriticalHeaderParams();
210        }
211
212
213        @Override
214        public boolean verify(final JWSHeader header,
215                              final byte[] signedContent, 
216                              final Base64URL signature)
217                throws JOSEException {
218
219                ensureSecretLengthSatisfiesAlgorithm(header.getAlgorithm());
220
221                if (! critPolicy.headerPasses(header)) {
222                        return false;
223                }
224
225                String jcaAlg = getJCAAlgorithmName(header.getAlgorithm());
226                byte[] expectedHMAC = HMAC.compute(jcaAlg, getSecretKey(), signedContent, getJCAContext().getProvider());
227                return ConstantTimeUtils.areEqual(expectedHMAC, signature.decode());
228        }
229}