// Path: fernet-java8/src/main/java/com/macasaet/fernet/Constants.java
// static final int cipherTextBlockSize = 16;
// 
// Path: fernet-java8/src/main/java/com/macasaet/fernet/Constants.java
// static final Decoder decoder = getUrlDecoder();
// 
// Path: fernet-java8/src/main/java/com/macasaet/fernet/Constants.java
// static final Encoder encoder = getUrlEncoder();
// 
// Path: fernet-java8/src/main/java/com/macasaet/fernet/Constants.java
// static final int initializationVectorBytes = 16;
// 
// Path: fernet-java8/src/main/java/com/macasaet/fernet/Constants.java
// static final int minimumTokenBytes = tokenStaticBytes + cipherTextBlockSize;
// 
// Path: fernet-java8/src/main/java/com/macasaet/fernet/Constants.java
// static final int signatureBytes = 32;
// 
// Path: fernet-java8/src/main/java/com/macasaet/fernet/Constants.java
// static final byte supportedVersion = (byte) 0x80;
// 
// Path: fernet-java8/src/main/java/com/macasaet/fernet/Constants.java
// static final int tokenStaticBytes = versionBytes + timestampBytes + initializationVectorBytes + signatureBytes;


import static com.macasaet.fernet.Constants.charset;
import static com.macasaet.fernet.Constants.cipherTextBlockSize;
import static com.macasaet.fernet.Constants.decoder;
import static com.macasaet.fernet.Constants.encoder;
import static com.macasaet.fernet.Constants.initializationVectorBytes;
import static com.macasaet.fernet.Constants.minimumTokenBytes;
import static com.macasaet.fernet.Constants.signatureBytes;
import static com.macasaet.fernet.Constants.supportedVersion;
import static com.macasaet.fernet.Constants.tokenStaticBytes;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.time.Instant;
import java.util.Base64.Encoder;
import java.util.Collection;
import javax.crypto.spec.IvParameterSpec;
/**
   Copyright 2017 Carlos Macasaet

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package com.macasaet.fernet;




/**
 * A Fernet token.
 *
 * <p>Copyright &copy; 2017 Carlos Macasaet.</p>
 *
 * @author Carlos Macasaet
 */
@SuppressWarnings({"PMD.TooManyMethods", "PMD.AvoidDuplicateLiterals"})
/*
 * TooManyMethods can be avoided by making the following API-breaking changes:
 * * remove the static `generate` methods and introduce a `TokenFactory` or `TokenBuilder`
 * * remove the public `validateAndDecrypt` methods since they are already available in the `Validator` interface
 * 
 * AvoidDuplicateLiterals is from the method-level @SuppressWarnings annotations
 */
public class Token {

    private final byte version;
    private final Instant timestamp;
    private final IvParameterSpec initializationVector;
    private final byte[] cipherText;
    private final byte[] hmac;

    /**
     * <p>Initialise a new Token from raw components. No validation of the signature is performed. However, the other
     * fields are validated to ensure they conform to the Fernet specification.</p>
     *
     * <p>Warning: Subsequent modifications to the input arrays will write through to this object.</p>
     *
     * @param version
     *            The version of the Fernet token specification. Currently, only 0x80 is supported.
     * @param timestamp
     *            the time the token was generated
     * @param initializationVector
     *            the randomly-generated bytes used to initialise the encryption cipher
     * @param cipherText
     *            the encrypted the encrypted payload
     * @param hmac
     *            the signature of the token
     */
    @SuppressWarnings({"PMD.ArrayIsStoredDirectly", "PMD.CyclomaticComplexity"})
    protected Token(final byte version, final Instant timestamp, final IvParameterSpec initializationVector,
            final byte[] cipherText, final byte[] hmac) {
        if (version != supportedVersion) {
            throw new IllegalTokenException("Unsupported version: " + version);
        }
        if (timestamp == null) {
            throw new IllegalTokenException("timestamp cannot be null");
        }
        if (initializationVector == null || initializationVector.getIV().length != initializationVectorBytes) {
            throw new IllegalTokenException("Initialization Vector must be 128 bits");
        }
// Path: fernet-java8/src/main/java/com/macasaet/fernet/Constants.java
// static final int cipherTextBlockSize = 16;
// 
// Path: fernet-java8/src/main/java/com/macasaet/fernet/Constants.java
// static final Decoder decoder = getUrlDecoder();
// 
// Path: fernet-java8/src/main/java/com/macasaet/fernet/Constants.java
// static final Encoder encoder = getUrlEncoder();
// 
// Path: fernet-java8/src/main/java/com/macasaet/fernet/Constants.java
// static final int initializationVectorBytes = 16;
// 
// Path: fernet-java8/src/main/java/com/macasaet/fernet/Constants.java
// static final int minimumTokenBytes = tokenStaticBytes + cipherTextBlockSize;
// 
// Path: fernet-java8/src/main/java/com/macasaet/fernet/Constants.java
// static final int signatureBytes = 32;
// 
// Path: fernet-java8/src/main/java/com/macasaet/fernet/Constants.java
// static final byte supportedVersion = (byte) 0x80;
// 
// Path: fernet-java8/src/main/java/com/macasaet/fernet/Constants.java
// static final int tokenStaticBytes = versionBytes + timestampBytes + initializationVectorBytes + signatureBytes;


// Path: fernet-java8/src/main/java/com/macasaet/fernet/Token.java
import static com.macasaet.fernet.Constants.charset;
import static com.macasaet.fernet.Constants.cipherTextBlockSize;
import static com.macasaet.fernet.Constants.decoder;
import static com.macasaet.fernet.Constants.encoder;
import static com.macasaet.fernet.Constants.initializationVectorBytes;
import static com.macasaet.fernet.Constants.minimumTokenBytes;
import static com.macasaet.fernet.Constants.signatureBytes;
import static com.macasaet.fernet.Constants.supportedVersion;
import static com.macasaet.fernet.Constants.tokenStaticBytes;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.time.Instant;
import java.util.Base64.Encoder;
import java.util.Collection;
import javax.crypto.spec.IvParameterSpec;

/**
   Copyright 2017 Carlos Macasaet

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package com.macasaet.fernet;




/**
 * A Fernet token.
 *
 * <p>Copyright &copy; 2017 Carlos Macasaet.</p>
 *
 * @author Carlos Macasaet
 */
@SuppressWarnings({"PMD.TooManyMethods", "PMD.AvoidDuplicateLiterals"})
/*
 * TooManyMethods can be avoided by making the following API-breaking changes:
 * * remove the static `generate` methods and introduce a `TokenFactory` or `TokenBuilder`
 * * remove the public `validateAndDecrypt` methods since they are already available in the `Validator` interface
 * 
 * AvoidDuplicateLiterals is from the method-level @SuppressWarnings annotations
 */
public class Token {

    private final byte version;
    private final Instant timestamp;
    private final IvParameterSpec initializationVector;
    private final byte[] cipherText;
    private final byte[] hmac;

    /**
     * <p>Initialise a new Token from raw components. No validation of the signature is performed. However, the other
     * fields are validated to ensure they conform to the Fernet specification.</p>
     *
     * <p>Warning: Subsequent modifications to the input arrays will write through to this object.</p>
     *
     * @param version
     *            The version of the Fernet token specification. Currently, only 0x80 is supported.
     * @param timestamp
     *            the time the token was generated
     * @param initializationVector
     *            the randomly-generated bytes used to initialise the encryption cipher
     * @param cipherText
     *            the encrypted the encrypted payload
     * @param hmac
     *            the signature of the token
     */
    @SuppressWarnings({"PMD.ArrayIsStoredDirectly", "PMD.CyclomaticComplexity"})
    protected Token(final byte version, final Instant timestamp, final IvParameterSpec initializationVector,
            final byte[] cipherText, final byte[] hmac) {
        if (version != supportedVersion) {
            throw new IllegalTokenException("Unsupported version: " + version);
        }
        if (timestamp == null) {
            throw new IllegalTokenException("timestamp cannot be null");
        }
        if (initializationVector == null || initializationVector.getIV().length != initializationVectorBytes) {
            throw new IllegalTokenException("Initialization Vector must be 128 bits");
        }
        if (cipherText == null || cipherText.length % cipherTextBlockSize != 0) {