package com.kryptnostic.kodex.v1.crypto.ciphers; import java.util.Arrays; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.base.Optional; import com.kryptnostic.kodex.v1.constants.Names; /** * Holds the output of performing an AES encryption with {@link PasswordCryptoService} * * @author Matthew Tamayo-Rios <matthew@kryptnostic.com> */ @JsonInclude( Include.NON_NULL ) public class BlockCiphertext { private final byte[] contents; private final byte[] iv; private final byte[] salt; private final Optional<byte[]> encryptedLength; private final Optional<byte[]> tag; public BlockCiphertext( byte[] iv, byte[] salt, byte[] contents ) { this( iv, salt, contents, Optional.<byte[]> absent(), Optional.<byte[]> absent() ); } @JsonCreator public BlockCiphertext( @JsonProperty( Names.IV ) byte[] iv, @JsonProperty( Names.SALT ) byte[] salt, @JsonProperty( Names.CONTENTS ) byte[] contents, @JsonProperty( Names.ENCRYPTED_LENGTH ) Optional<byte[]> encryptedLength, @JsonProperty( Names.TAG ) Optional<byte[]> tag) { this.contents = contents; this.iv = iv; this.salt = salt; this.encryptedLength = encryptedLength; this.tag = tag; } @JsonProperty( Names.CONTENTS ) public byte[] getContents() { return contents; } @JsonProperty( Names.IV ) public byte[] getIv() { return iv; } @JsonProperty( Names.SALT ) public byte[] getSalt() { return salt; } @JsonProperty( Names.ENCRYPTED_LENGTH ) public Optional<byte[]> getEncryptedLength() { return encryptedLength; } @JsonProperty( Names.TAG ) public Optional<byte[]> getTag() { return tag; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + Arrays.hashCode( tag.or( new byte[] {} ) ); result = prime * result + Arrays.hashCode( encryptedLength.or( new byte[] {} ) ); result = prime * result + Arrays.hashCode( iv ); result = prime * result + Arrays.hashCode( salt ); return result; } @Override public boolean equals( Object obj ) { if ( this == obj ) { return true; } else if ( obj instanceof BlockCiphertext ) { BlockCiphertext other = (BlockCiphertext) obj; boolean ivEquals = Arrays.equals( iv, other.iv ); boolean saltEquals = Arrays.equals( salt, other.salt ); // Only true when both null or both the same actual object, which is checked above boolean encryptedLengthEquals = ( encryptedLength == other.encryptedLength ); if ( ( encryptedLength != null ) && ( other.encryptedLength != null ) ) { encryptedLengthEquals = Arrays.equals( encryptedLength.or( new byte[] {} ), other.encryptedLength.or( new byte[] {} ) ); } boolean tagEquals = ( tag == other.tag ); if ( ( tag != null ) & ( other.tag != null ) ) { tagEquals = Arrays.equals( tag.or( new byte[] {} ), other.tag.or( new byte[] {} ) ); } return ivEquals && saltEquals && encryptedLengthEquals && tagEquals; } return false; } public static class Builder { private byte[] iv; private byte[] salt; private byte[] contents; private byte[] encryptedLength; private byte[] tag; public Builder setIv( byte[] iv ) { this.iv = iv; return this; } public Builder setSalt( byte[] salt ) { this.salt = salt; return this; } public Builder setContents( byte[] contents ) { this.contents = contents; return this; } public Builder setEncryptedLength( byte[] encryptedLength ) { this.encryptedLength = encryptedLength; return this; } public Builder setTag( byte[] tag ) { this.tag = tag; return this; } public BlockCiphertext createBlockCiphertext() { return new BlockCiphertext( iv, salt, contents, Optional.fromNullable( encryptedLength ), Optional.fromNullable( tag ) ); } } }