/* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.generator.sp80038d; import java.util.concurrent.atomic.AtomicLong; import org.cryptacular.generator.LimitException; import org.cryptacular.generator.Nonce; import org.cryptacular.util.ByteUtil; /** * Deterministic nonce generation strategy that uses a counter for the invocation field as described in NIST <a * href="http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf">SP-800-38D</a>, section 8.2.1. The * invocation part of the sequence is always 64 bits (8 bytes) due to the use of a <code>long</code>, thus the length of * the nonce is determined by the length of the fixed part: <code>length = 8 + fixed.length</code>. * * <p><strong>NOTE:</strong> users of this class are responsible for maintaining the invocation count in order to * support enforcement of constraints described in section 8.3; namely the following:</p> * * <blockquote>The total number of invocations of the authenticated encryption function shall not exceed 2<sup>32</sup>, * including all IV lengths and all instances of the authenticated encryption function with the given key.</blockquote> * * <p>Instances of this class enforce this constraint by considering the nonce length, which determines whether the * constraint applies, and the invocation count. The invocation count is incremented upon every invocation of {@link * #generate()} method. The current invocation count is accessible via {@link #getInvocations()}.</p> * * <p>Instances of this class are thread safe.</p> * * @author Middleware Services */ public class CounterNonce implements Nonce { /** Default nonce getLength is {@value} bytes. */ public static final int DEFAULT_LENGTH = 12; /** * Maximum invocations is 2<sup>32</sup>. Does not apply to nonces with default getLength, {@value #DEFAULT_LENGTH}. */ public static final long MAX_INVOCATIONS = 0xFFFFFFFFL; /** Fixed field value. */ private final byte[] fixed; /** Invocation count. */ private final AtomicLong count; /** * Creates a new instance. * * @param fixed User-defined fixed field value. * @param invocations Initial invocation count. The invocations field is incremented _before_ use in {@link * #generate()}. */ public CounterNonce(final String fixed, final long invocations) { this(ByteUtil.toBytes(fixed), invocations); } /** * Creates a new instance. Instances of this method produces nonces of the default length, {@value #DEFAULT_LENGTH}, * and are not subject to constraints on the number of invocations. * * @param fixed User-defined fixed field value. * @param invocations Initial invocation count. The invocations field is incremented _before_ use in {@link * #generate()}. */ public CounterNonce(final int fixed, final long invocations) { this(ByteUtil.toBytes(fixed), invocations); } /** * Creates a new instance. * * @param fixed User-defined fixed field value. * @param invocations Initial invocation count. The invocations field is incremented _before_ use in {@link * #generate()}. */ public CounterNonce(final long fixed, final long invocations) { this(ByteUtil.toBytes(fixed), invocations); } /** * Creates a new instance. * * @param fixed User-defined fixed field value. * @param invocations Initial invocation count. The invocations field is incremented _before_ use in {@link * #generate()}. */ public CounterNonce(final byte[] fixed, final long invocations) { if (fixed == null || fixed.length == 0) { throw new IllegalArgumentException("Fixed part cannot be null or empty."); } this.count = new AtomicLong(invocations); this.fixed = fixed; } @Override public byte[] generate() throws LimitException { final byte[] value = new byte[getLength()]; System.arraycopy(fixed, 0, value, 0, fixed.length); final long next = count.incrementAndGet(); if (value.length != DEFAULT_LENGTH) { // Enforce constraints described in section 8.3 if (next > MAX_INVOCATIONS) { throw new LimitException("Exceeded 2^32 invocations."); } } ByteUtil.toBytes(next, value, fixed.length); return value; } @Override public int getLength() { return fixed.length + 8; } /** @return Current invocation count. */ public long getInvocations() { return count.get(); } }