All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.aholbrook.paseto.PasetoV2 Maven / Gradle / Ivy

package net.aholbrook.paseto;

import net.aholbrook.paseto.base64.Base64Provider;
import net.aholbrook.paseto.crypto.KeyPair;
import net.aholbrook.paseto.crypto.NonceGenerator;
import net.aholbrook.paseto.crypto.v2.V2CryptoProvider;
import net.aholbrook.paseto.encoding.EncodingProvider;
import net.aholbrook.paseto.exception.DecryptionException;
import net.aholbrook.paseto.exception.PasetoParseException;
import net.aholbrook.paseto.exception.SignatureVerificationException;
import net.aholbrook.paseto.util.PaeUtil;
import net.aholbrook.paseto.util.StringUtils;

public class PasetoV2 extends Paseto {
	private final static String VERSION = "v2";
	public final static String HEADER_LOCAL = VERSION + SEPARATOR + PURPOSE_LOCAL + SEPARATOR; // v2.local.
	public final static String HEADER_PUBLIC = VERSION + SEPARATOR + PURPOSE_PUBLIC + SEPARATOR; // v2.public.

	private final V2CryptoProvider cryptoProvider;

	private PasetoV2(Base64Provider base64Provider, EncodingProvider encodingProvider, V2CryptoProvider cryptoProvider,
			NonceGenerator nonceGenerator) {
		super(base64Provider, encodingProvider, nonceGenerator);
		this.cryptoProvider = cryptoProvider;
	}

	@Override
	public String encrypt(Object payload, byte[] key, String footer) {
		footer = StringUtils.ntes(footer); // convert null to ""
		byte[] payloadBytes = StringUtils.getBytesUtf8(encodingProvider.encode(payload));
		byte[] footerBytes = StringUtils.getBytesUtf8(footer);

		byte nonce[] = nonceGenerator.generateNonce();
		byte[] n = new byte[cryptoProvider.xChaCha20Poly1305IetfNpubbytes()];
		cryptoProvider.blake2b(n, payloadBytes, nonce);

		byte[] preAuth = PaeUtil.pae(StringUtils.getBytesUtf8(HEADER_LOCAL), n, footerBytes);

		byte[] c = new byte[payloadBytes.length + cryptoProvider.xChaCha20Poly1305IetfAbytes()];
		cryptoProvider.aeadXChaCha20Poly1305IetfEncrypt(c, payloadBytes, preAuth, n, key);

		byte[] nc = new byte[n.length + c.length];
		System.arraycopy(n, 0, nc, 0, n.length);
		System.arraycopy(c, 0, nc, n.length, c.length);

		if (footerBytes.length > 0) {
			return HEADER_LOCAL + base64Provider.encodeToString(nc) + SEPARATOR
					+ base64Provider.encodeToString(footerBytes);
		} else {
			return HEADER_LOCAL + base64Provider.encodeToString(nc);
		}
	}

	@Override
	public <_Payload> _Payload decrypt(String token, byte[] key, String footer, Class<_Payload> payloadClass) {
		// Split token into sections
		String[] sections = split(token);
		if (sections == null) {
			throw new PasetoParseException(PasetoParseException.Reason.MISSING_SECTIONS, token);
		}

		// Check header
		checkHeader(token, sections, HEADER_LOCAL);

		// Decode footer
		String decodedFooter = decodeFooter(token, sections, footer);

		// Decrypt
		byte[] nc = base64Provider.decodeFromString(sections[2]);
		byte[] n = new byte[cryptoProvider.xChaCha20Poly1305IetfNpubbytes()];
		// verify length
		if (nc.length < n.length + 1) {
			throw new PasetoParseException(PasetoParseException.Reason.PAYLOAD_LENGTH, token)
					.setMinLength(n.length + 1);
		}
		byte[] c = new byte[nc.length - n.length];
		System.arraycopy(nc, 0, n, 0, n.length);
		System.arraycopy(nc, n.length, c, 0, c.length);

		byte[] preAuth = PaeUtil.pae(StringUtils.getBytesUtf8(HEADER_LOCAL), n, StringUtils.getBytesUtf8(decodedFooter));
		byte[] p = new byte[c.length - cryptoProvider.xChaCha20Poly1305IetfAbytes()];
		if (!cryptoProvider.aeadXChaCha20Poly1305IetfDecrypt(p, c, preAuth, n, key)) {
			throw new DecryptionException(token);
		}

		// Convert from JSON
		return decode(p, payloadClass);
	}

	@Override
	public String sign(Object payload, byte[] sk, String footer) {
		footer = StringUtils.ntes(footer); // convert null to ""
		byte[] payloadBytes = StringUtils.getBytesUtf8(encodingProvider.encode(payload));
		byte[] footerBytes = StringUtils.getBytesUtf8(footer);

		byte[] m2 = PaeUtil.pae(StringUtils.getBytesUtf8(HEADER_PUBLIC), payloadBytes, footerBytes);
		byte[] sig = new byte[cryptoProvider.ed25519SignBytes()];
		cryptoProvider.ed25519Sign(sig, m2, sk);

		byte[] msig = new byte[payloadBytes.length + sig.length];
		System.arraycopy(payloadBytes, 0, msig, 0, payloadBytes.length);
		System.arraycopy(sig, 0, msig, payloadBytes.length, sig.length);

		if (footerBytes.length > 0) {
			return HEADER_PUBLIC + base64Provider.encodeToString(msig)
					+ SEPARATOR + base64Provider.encodeToString(footerBytes);
		} else {
			return HEADER_PUBLIC + base64Provider.encodeToString(msig);
		}
	}

	@Override
	public <_Payload> _Payload verify(String token, byte[] pk, String footer, Class<_Payload> payloadClass) {
		// Split token into sections
		String[] sections = split(token);
		if (sections == null) {
			throw new PasetoParseException(PasetoParseException.Reason.MISSING_SECTIONS, token);
		}

		// Check header
		checkHeader(token, sections, HEADER_PUBLIC);

		// Decode footer
		String decodedFooter = decodeFooter(token, sections, footer);

		// Verify
		byte[] msig = base64Provider.decodeFromString(sections[2]);
		byte[] s = new byte[cryptoProvider.ed25519SignBytes()];
		// verify length
		if (msig.length < s.length + 1) {
			throw new PasetoParseException(PasetoParseException.Reason.PAYLOAD_LENGTH, token)
					.setMinLength(s.length + 1);
		}
		byte[] m = new byte[msig.length - s.length];
		System.arraycopy(msig, msig.length - s.length, s, 0, s.length);
		System.arraycopy(msig, 0, m, 0, m.length);

		byte[] m2 = PaeUtil.pae(StringUtils.getBytesUtf8(HEADER_PUBLIC), m, StringUtils.getBytesUtf8(decodedFooter));
		if (!cryptoProvider.ed25519Verify(s, m2, pk)) {
			throw new SignatureVerificationException(token);
		}

		// Convert from JSON
		return decode(m, payloadClass);
	}

	@Override
	public KeyPair generateKeyPair() {
		return cryptoProvider.ed25519Generate();
	}

	public static class Builder {
		private final Base64Provider base64Provider;
		private final EncodingProvider encodingProvider;
		private final V2CryptoProvider v2CryptoProvider;
		private NonceGenerator nonceGenerator;

		public Builder(Base64Provider base64Provider, EncodingProvider encodingProvider, V2CryptoProvider v2CryptoProvider) {
			if (base64Provider == null) { throw new NullPointerException("Base64 provider is required."); }
			if (encodingProvider == null) { throw new NullPointerException("Encoding provider is required."); }
			if (v2CryptoProvider == null) { throw new NullPointerException("V2 Crypto Provider is required."); }

			this.base64Provider = base64Provider;
			this.encodingProvider = encodingProvider;
			this.v2CryptoProvider = v2CryptoProvider;
		}

		public Builder withTestingNonceGenerator(NonceGenerator nonceGenerator) {
			this.nonceGenerator = nonceGenerator;
			return this;
		}

		public PasetoV2 build() {
			if (nonceGenerator == null) { nonceGenerator = v2CryptoProvider.getNonceGenerator(); }
			return new PasetoV2(base64Provider, encodingProvider, v2CryptoProvider, nonceGenerator);
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy