com.amazonaws.encryptionsdk.model.CipherBlockHeaders Maven / Gradle / Ivy
/*
* Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
* in compliance with the License. A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file 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.amazonaws.encryptionsdk.model;
import java.nio.ByteBuffer;
import java.util.Arrays;
import com.amazonaws.encryptionsdk.exception.AwsCryptoException;
import com.amazonaws.encryptionsdk.exception.BadCiphertextException;
import com.amazonaws.encryptionsdk.exception.ParseException;
import com.amazonaws.encryptionsdk.internal.Constants;
import com.amazonaws.encryptionsdk.internal.PrimitivesParser;
/**
* This class implements the headers for the encrypted content stored in a
* single block. These headers are parsed and used when the encrypted content
* in the single block is decrypted.
*
*
* It contains the following fields in order:
*
* - nonce
* - length of content
*
*/
//@ non_null_by_default
public final class CipherBlockHeaders {
//@ spec_public nullable
private byte[] nonce_;
//@ spec_public
private long contentLength_ = -1;
// This is set after the nonce length is parsed in the CiphertextHeaders
// during decryption. This can be set only using its setter.
//@ spec_public
private short nonceLength_ = 0;
//@ public invariant nonceLength_ >= 0;
//@ spec_public
private boolean isComplete_;
/**
* Default constructor.
*/
//@ public normal_behavior
//@ ensures nonce_ == null;
//@ ensures contentLength_ == -1;
//@ ensures nonceLength_ == 0;
//@ ensures isComplete_ == false;
public CipherBlockHeaders() {
}
/**
* Construct the single block headers using the provided nonce
* and length of content.
*
* @param nonce
* the bytes containing the nonce.
* @param contentLen
* the length of the content in the block.
*/
//@ public normal_behavior
//@ requires nonce != null && nonce.length <= Constants.MAX_NONCE_LENGTH;
//@ ensures \fresh(nonce_) && nonce_.length == nonce.length;
//@ ensures Arrays.equalArrays(nonce_, nonce);
//@ ensures contentLength_ == contentLen;
//@ ensures nonceLength_ == 0;
//@ ensures isComplete_ == false;
//@ also private exceptional_behavior
//@ requires nonce == null || nonce.length > Constants.MAX_NONCE_LENGTH;
//@ signals_only AwsCryptoException;
//@ pure
public CipherBlockHeaders(/*@ nullable @*/ final byte[] nonce, final long contentLen) {
if (nonce == null) {
throw new AwsCryptoException("Nonce cannot be null.");
}
if (nonce.length > Constants.MAX_NONCE_LENGTH) {
throw new AwsCryptoException(
"Nonce length is greater than the maximum value of an unsigned byte.");
}
nonce_ = nonce.clone();
contentLength_ = contentLen;
}
/**
* Serialize the header into a byte array.
*
* @return
* the serialized bytes of the header.
*/
/*@ public normal_behavior
@ requires nonce_ != null;
@ old int nLen = nonce_.length;
@ requires nonce_.length <= Integer.MAX_VALUE - (Long.SIZE / Byte.SIZE);
@ ensures \result.length == nonce_.length + (Long.SIZE / Byte.SIZE);
@ ensures (\forall int i; 0<=i && i
* If successful, it returns the size of the parsed bytes which is the nonce
* length. On failure, it throws a parse exception.
*
* @param b
* the byte array to parse.
* @param off
* the offset in the byte array to use when parsing.
* @return
* the size of the parsed bytes which is the nonce length.
* @throws ParseException
* if there are not sufficient bytes to parse the nonce.
*/
//@ private normal_behavior
//@ requires nonceLength_ > 0;
//@ requires 0 <= off;
//@ requires b.length - off >= nonceLength_;
//@ assignable nonce_;
//@ ensures nonce_ != null && \fresh(nonce_);
//@ ensures Arrays.equalArrays(b, off, nonce_, 0, nonceLength_);
//@ ensures \result == nonceLength_;
//@ also private exceptional_behavior
//@ // add exceptions from arrays.copyofrange
//@ requires b.length - off < nonceLength_;
//@ assignable \nothing;
//@ signals_only ParseException;
private int parseNonce(final byte[] b, final int off) throws ParseException {
final int bytesToParseLen = b.length - off;
if (bytesToParseLen >= nonceLength_) {
nonce_ = Arrays.copyOfRange(b, off, off + nonceLength_);
return nonceLength_;
} else {
throw new ParseException("Not enough bytes to parse nonce");
}
}
/**
* Parse the content length in the provided bytes. It looks for 8 bytes
* representing a long primitive type in the provided bytes starting at the
* specified off.
*
*
* If successful, it returns the size of the parsed bytes which is the size
* of the long primitive type. On failure, it throws a parse exception.
*
* @param b
* the byte array to parse.
* @param off
* the offset in the byte array to use when parsing.
* @return
* the size of the parsed bytes which is the size of the long
* primitive type.
* @throws ParseException
* if there are not sufficient bytes to parse the content
* length.
*/
//@ private behavior
//@ requires off >= 0;
//@ requires b.length - off >= Long.BYTES;
//@ old long len = Long.asLong(b[off],b[off+1],b[off+2],b[off+3],b[off+4],b[off+5],b[off+6],b[off+7]);
//@ assignable contentLength_;
//@ ensures len >= 0;
//@ ensures contentLength_ == len;
//@ ensures \result == Long.BYTES;
//@ signals_only BadCiphertextException;
//@ signals (BadCiphertextException) len < 0 && contentLength_ == len;
//@ also private exceptional_behavior
//@ requires b.length - off < Long.BYTES;
//@ assignable \nothing;
//@ signals_only ParseException;
private int parseContentLength(final byte[] b, final int off) throws ParseException {
contentLength_ = PrimitivesParser.parseLong(b, off);
if (contentLength_ < 0) {
throw new BadCiphertextException("Invalid content length in ciphertext");
}
return Long.SIZE / Byte.SIZE;
}
/**
* Deserialize the provided bytes starting at the specified offset to
* construct an instance of this class.
*
*
* This method parses the provided bytes for the individual fields in this
* class. This methods also supports partial parsing where not all the bytes
* required for parsing the fields successfully are available.
*
* @param b
* the byte array to deserialize.
* @param off
* the offset in the byte array to use for deserialization.
* @return
* the number of bytes consumed in deserialization.
*/
/*@ public normal_behavior
@ requires b == null;
@ assignable \nothing;
@ ensures \result == 0;
@ also
@ // case: do not need to parse either value
@ public normal_behavior
@ requires b != null && contentLength_ >= 0 && (nonce_ != null || nonceLength_ == 0);
@ assignable isComplete_;
@ ensures \result == 0;
@ ensures isComplete_;
@ also
@ // case: parse nonce (parse exception)
@ public normal_behavior
@ requires b != null && nonce_ == null && nonceLength_ > 0;
@ requires b.length - off < nonceLength_;
@ assignable \nothing;
@ ensures \result == 0;
@ also
@ // case: parse nonce (normally) and not content length
@ public normal_behavior
@ requires b != null && nonce_ == null && nonceLength_ > 0;
@ requires off >= 0 && b.length - off >= nonceLength_;
@ requires contentLength_ >= 0;
@ assignable nonce_, isComplete_;
@ ensures nonce_ != null && \fresh(nonce_);
@ ensures Arrays.equalArrays(b, off, nonce_, 0, nonceLength_);
@ ensures \result == nonceLength_;
@ ensures isComplete_;
@ also
@ // case: do not parse nonce and parse content length (parse exception)
@ public normal_behavior
@ requires b != null && (nonce_ != null || nonceLength_ == 0);
@ requires contentLength_ < 0;
@ requires b.length - off < Long.BYTES;
@ assignable \nothing;
@ ensures \result == 0;
@ also
@ // case: parse nonce (normally) and parse content length (parse exception)
@ public normal_behavior
@ requires b != null && nonce_ == null && nonceLength_ > 0;
@ requires off >= 0 && b.length - off >= nonceLength_;
@ requires contentLength_ < 0;
@ requires b.length - (off + nonceLength_) < Long.BYTES;
@ assignable nonce_;
@ ensures Arrays.equalArrays(b, off, nonce_, 0, nonceLength_);
@ ensures \result == nonceLength_;
@ also
@ // case: do not parse nonce and parse content length (normally)
@ public behavior
@ requires b != null && (nonce_ != null || nonceLength_ == 0);
@ requires contentLength_ < 0;
@ requires off >= 0;
@ requires b.length - off >= Long.BYTES;
@ assignable contentLength_, isComplete_;
@ ensures isComplete_ && contentLength_ >= 0;
@ ensures contentLength_ == Long.asLong(b[off], b[off+1], b[off+2], b[off+3],
@ b[off+4], b[off+5], b[off+6], b[off+7]);
@ ensures \result == Long.BYTES;
@ signals_only BadCiphertextException;
@ signals (BadCiphertextException) contentLength_ < 0 && isComplete_ == \old(isComplete_);
@ also
@ // case: parse both normally
@ public behavior
@ old int nLen = nonceLength_;
@ requires b != null;
@ requires nonce_ == null && nonceLength_ > 0 && contentLength_ < 0;
@ requires off >= 0 && b.length - off >= nonceLength_;
@ requires b.length - (off + nonceLength_) >= Long.BYTES;
@ requires nonceLength_ <= Integer.MAX_VALUE - Long.BYTES;
@ assignable nonce_, contentLength_, isComplete_;
@ ensures isComplete_ && contentLength_ >= 0;
@ ensures Arrays.equalArrays(b, off, nonce_, 0, nonceLength_);
@ ensures contentLength_ == Long.asLong(b[nLen+off], b[nLen+off+1], b[nLen+off+2],
@ b[nLen+off+3], b[nLen+off+4], b[nLen+off+5],
@ b[nLen+off+6], b[nLen+off+7]);
@ ensures \result == nonceLength_ + Long.BYTES;
@ signals_only BadCiphertextException;
@ signals (BadCiphertextException) (contentLength_ < 0 && isComplete_ == \old(isComplete_)
@ && Arrays.equalArrays(b, off, nonce_, 0, nonceLength_));
@*/
public int deserialize(/*@ nullable */ final byte[] b, final int off) {
if (b == null) {
return 0;
}
//@ assert b != null;
int parsedBytes = 0;
try {
if (nonceLength_ > 0 && nonce_ == null) {
parsedBytes += parseNonce(b, off + parsedBytes);
}
if (contentLength_ < 0) {
parsedBytes += parseContentLength(b, off + parsedBytes);
}
isComplete_ = true;
} catch (ParseException e) {
// this results when we do partial parsing and there aren't enough
// bytes to parse; so just return the bytes parsed thus far.
}
return parsedBytes;
}
/**
* Check if this object has all the header fields populated and available
* for reading.
*
* @return
* true if this object containing the single block header fields
* is complete; false otherwise.
*/
//@ public normal_behavior
//@ ensures \result == isComplete_;
//@ pure
public boolean isComplete() {
return isComplete_;
}
/**
* Return the nonce set in the single block header.
*
* @return
* the bytes containing the nonce set in the single block header.
*/
//@ public normal_behavior
//@ requires nonce_ == null;
//@ ensures \result == null;
//@ also public normal_behavior
//@ requires nonce_ != null;
//@ ensures \result != null;
//@ ensures \fresh(\result);
//@ ensures \result != null;
//@ ensures \result.length == nonce_.length;
//@ ensures java.util.Arrays.equalArrays(\result,nonce_);
//@ pure nullable
public byte[] getNonce() {
return nonce_ != null ? nonce_.clone() : null;
}
/**
* Return the content length set in the single block header.
*
* @return
* the content length set in the single block header.
*/
//@ public normal_behavior
//@ ensures \result == contentLength_;
//@ pure
public long getContentLength() {
return contentLength_;
}
/**
* Set the length of the nonce used in the encryption of the content stored
* in the single block.
*
* @param nonceLength
* the length of the nonce used in the encryption of the content
* stored in the single block.
*/
//@ public normal_behavior
//@ requires nonceLength >= 0;
//@ assignable nonceLength_;
//@ ensures nonceLength_ == nonceLength;
public void setNonceLength(final short nonceLength) {
nonceLength_ = nonceLength;
}
}