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.StandardCharset;
032import net.jcip.annotations.ThreadSafe;
033
034import javax.crypto.SecretKey;
035import java.util.Set;
036
037
038/**
039 * Message Authentication Code (MAC) verifier of 
040 * {@link com.nimbusds.jose.JWSObject JWS objects}. Expects a secret key.
041 *
042 * <p>See RFC 7518
043 * <a href="https://tools.ietf.org/html/rfc7518#section-3.2">section 3.2</a>
044 * for more information.
045 *
046 * <p>This class is thread-safe.
047 *
048 * <p>Supports the following algorithms:
049 *
050 * <ul>
051 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#HS256}
052 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#HS384}
053 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#HS512}
054 * </ul>
055 *
056 <p>Tested with the AWS CloudHSM JCE provider.
057 * 
058 * @author Vladimir Dzhuvinov
059 * @version 2016-06-26
060 */
061@ThreadSafe
062public class MACVerifier extends MACProvider implements JWSVerifier, CriticalHeaderParamsAware {
063
064
065        /**
066         * The critical header policy.
067         */
068        private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral();
069
070
071        /**
072         * Creates a new Message Authentication (MAC) verifier.
073         *
074         * @param secret The secret. Must be at least 256 bits long and not
075         *               {@code null}.
076         *
077         * @throws JOSEException If the secret length is shorter than the
078         *                       minimum 256-bit requirement.
079         */
080        public MACVerifier(final byte[] secret)
081                throws JOSEException {
082
083                this(secret, null);
084        }
085
086
087        /**
088         * Creates a new Message Authentication (MAC) verifier.
089         *
090         * @param secretString The secret as a UTF-8 encoded string. Must be at
091         *                     least 256 bits long and not {@code null}.
092         *
093         * @throws JOSEException If the secret length is shorter than the
094         *                       minimum 256-bit requirement.
095         */
096        public MACVerifier(final String secretString)
097                throws JOSEException {
098
099                this(secretString.getBytes(StandardCharset.UTF_8));
100        }
101
102
103        /**
104         * Creates a new Message Authentication (MAC) verifier.
105         *
106         * @param secretKey The secret key. Must be at least 256 bits long and
107         *                  not {@code null}.
108         *
109         * @throws JOSEException If the secret length is shorter than the
110         *                       minimum 256-bit requirement.
111         */
112        public MACVerifier(final SecretKey secretKey)
113                throws JOSEException {
114
115                this(secretKey, null);
116        }
117
118
119        /**
120         * Creates a new Message Authentication (MAC) verifier.
121         *
122         * @param jwk The secret as a JWK. Must be at least 256 bits long and
123         *            not {@code null}.
124         *
125         * @throws JOSEException If the secret length is shorter than the
126         *                       minimum 256-bit requirement.
127         */
128        public MACVerifier(final OctetSequenceKey jwk)
129                throws JOSEException {
130
131                this(jwk.toByteArray());
132        }
133
134
135        /**
136         * Creates a new Message Authentication (MAC) verifier.
137         *
138         * @param secret         The secret. Must be at least 256 bits long
139         *                       and not {@code null}.
140         * @param defCritHeaders The names of the critical header parameters
141         *                       that are deferred to the application for
142         *                       processing, empty set or {@code null} if none.
143         *
144         * @throws JOSEException If the secret length is shorter than the
145         *                       minimum 256-bit requirement.
146         */
147        public MACVerifier(final byte[] secret,
148                           final Set<String> defCritHeaders)
149                throws JOSEException {
150
151                super(secret, SUPPORTED_ALGORITHMS);
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, SUPPORTED_ALGORITHMS);
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                if (! critPolicy.headerPasses(header)) {
220                        return false;
221                }
222
223                String jcaAlg = getJCAAlgorithmName(header.getAlgorithm());
224                byte[] expectedHMAC = HMAC.compute(jcaAlg, getSecretKey(), signedContent, getJCAContext().getProvider());
225                return ConstantTimeUtils.areEqual(expectedHMAC, signature.decode());
226        }
227}