// 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;

                return new Token(version, Instant.ofEpochSecond(timestampSeconds),
                        new IvParameterSpec(initializationVector), cipherText, hmac);
            }
        } catch (final IOException ioe) {
            // this should not happen as I/O is from memory and stream
            // length is verified ahead of time
            throw new IllegalStateException(ioe.getMessage(), ioe);
        }
    }

    protected static byte[] read(final DataInputStream stream, final int numBytes) throws IOException {
        final byte[] retval = new byte[numBytes];
        final int bytesRead = stream.read(retval);
        if (bytesRead < numBytes) {
            throw new IllegalTokenException("Not enough bits to generate a Token");
        }
        return retval;
    }

    /**
     * Deserialise a Base64 URL Fernet token string. This does NOT validate that the token was generated using a valid {@link Key}.
     *
     * @param string
     *            the Base 64 URL encoding of a token in the form Version | Timestamp | IV | Ciphertext | HMAC
     * @return a new Token
     * @throws IllegalTokenException
     *             if the input string cannot be a valid token irrespective of key or timestamp
     */
    public static Token fromString(final String string) {
// 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;


                return new Token(version, Instant.ofEpochSecond(timestampSeconds),
                        new IvParameterSpec(initializationVector), cipherText, hmac);
            }
        } catch (final IOException ioe) {
            // this should not happen as I/O is from memory and stream
            // length is verified ahead of time
            throw new IllegalStateException(ioe.getMessage(), ioe);
        }
    }

    protected static byte[] read(final DataInputStream stream, final int numBytes) throws IOException {
        final byte[] retval = new byte[numBytes];
        final int bytesRead = stream.read(retval);
        if (bytesRead < numBytes) {
            throw new IllegalTokenException("Not enough bits to generate a Token");
        }
        return retval;
    }

    /**
     * Deserialise a Base64 URL Fernet token string. This does NOT validate that the token was generated using a valid {@link Key}.
     *
     * @param string
     *            the Base 64 URL encoding of a token in the form Version | Timestamp | IV | Ciphertext | HMAC
     * @return a new Token
     * @throws IllegalTokenException
     *             if the input string cannot be a valid token irrespective of key or timestamp
     */
    public static Token fromString(final String string) {
        return fromBytes(decoder.decode(string));