package gnu.crypto.exp.ust; // ---------------------------------------------------------------------------- // $Id: UST.java,v 1.5 2005/10/06 04:24:14 rsdio Exp $ // // Copyright (C) 2001, 2002, 2003, Free Software Foundation, Inc. // // This file is part of GNU Crypto. // // GNU Crypto is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2, or (at your option) // any later version. // // GNU Crypto is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; see the file COPYING. If not, write to the // // Free Software Foundation Inc., // 51 Franklin Street, Fifth Floor, // Boston, MA 02110-1301 // USA // // Linking this library statically or dynamically with other modules is // making a combined work based on this library. Thus, the terms and // conditions of the GNU General Public License cover the whole // combination. // // As a special exception, the copyright holders of this library give // you permission to link this library with independent modules to // produce an executable, regardless of the license terms of these // independent modules, and to copy and distribute the resulting // executable under terms of your choice, provided that you also meet, // for each linked independent module, the terms and conditions of the // license of that module. An independent module is a module which is // not derived from or based on this library. If you modify this // library, you may extend this exception to your version of the // library, but you are not obligated to do so. If you do not wish to // do so, delete this exception statement from your version. // ---------------------------------------------------------------------------- import gnu.crypto.Registry; import gnu.crypto.cipher.IBlockCipher; import gnu.crypto.mac.TMMH16; import gnu.crypto.prng.ICMGenerator; import gnu.crypto.prng.IRandom; import gnu.crypto.prng.PRNGFactory; import gnu.crypto.prng.UMacGenerator; import gnu.crypto.prng.LimitReachedException; import java.math.BigInteger; import java.security.InvalidKeyException; import java.util.HashMap; import java.util.Map; /** * <p>The Universal Security Transform (UST) is a cryptographic transform for * providing confidentiality, message authentication, and replay protection. * This transform is sufficient for providing these services to network * protocols, though it does not specify a protocol itself.</p> * * <p>UST has the following parameters:</p> * * <ul> * <li><b>INDEX_LENGTH</b>: The number of octets in an Index.</li> * * <li><b>MAX_KEYSTREAM_LENGTH</b>: The maximum number of octets in a * keystream segment.</li> * * <li><b>PREFIX_LENGTH</b>: The number of octets in the keystream prefix.</li> * * <li><b>TAG_LENGTH</b>: The number of octets in an Authentication Tag.</li> * * <li><b>MAX_HASH_LENGTH</b>: The maximum number of octets that can be input * to the hash function.</li> * * <li><b>HASH_KEY_LENGTH</b>: The number of octets in a hash key.</li> * </ul> * * <p>All of these parameters <b>MUST</b> remain fixed for any given UST * context.</p> * * <p>The parameters <code>INDEX_LENGTH</code> and <code>MAX_KEYSTREAM_LENGTH</code> * are defined by the keystream generator. This is because the keystream * generators used in UST are families of <em>length-expanding pseudorandom * functions</em>. At the time of implementing UST, the only such generators * available are (a) {@link gnu.crypto.prng.UMacGenerator} and (b) {@link * gnu.crypto.prng.ICMGenerator}. Yet, both of these two algorithms require more * than just these two parameters to be fully qualified, and hence instantiated. * They both require an underlying <em>block cipher</em> as well as a <em>block * size</em> and <em>key material</em>. While default values are used for the * former two of these parameters -if they are not included in their * initialisation {@link java.util.Map}- the latter parameter; i.e. <em>key * material</em>, should be specified.</p> * * <p>The parameters <code>TAG_LENGTH</code>, <code>MAX_HASH_LENGTH</code>, and * <code>HASH_KEY_LENGTH</code> are defined by the hash function. Please note * that when the UST document refers to a <em>hash function</em> it actually * means a <em>Universal Hash Function</em>. At the time of implementing UST, * the only such functions available are {@link gnu.crypto.mac.UHash32} and * {@link gnu.crypto.mac.TMMH16}, with the latter more suited for UST purposes * because it is independent from any other algorithm --the same is not true for * (our implementation of) <code>UHash32</code> because of its current * dependencies on {@link gnu.crypto.mac.UMac32}; although this may change in * the future. For this reason we shall only allow {@link gnu.crypto.mac.TMMH16} * as the UST's underlying <em>Universal Hash Function</em>.</p> * * <p>The length of any <em>Plaintext</em> protected by UST <b>MUST NOT</b> * exceed the smaller of <code>(MAX_KEYSTREAM_LENGTH - TAG_LENGTH)</code> and * <code>MAX_HASH_LEN</code>.</p> * * <p>The value of <code>HASH_KEY_LENGTH</code> <b>MUST</b> be no greater than * <code>MAX_KEYSTREAM_LENGTH</code>. The value of <code>TAG_LENGTH</code> * <b>MUST</b> be no greater than <code>HASH_KEY_LENGTH</code>.</p> * * <p>Once a UST instance is initialised, one can (a) authenticate designated * bytes -an operation that does not yield any output- and/or (b) encipher * designated bytes -an operation that does yield an output equal in length to * the input.</p> * * <p>The understanding of the author of this implementation is that an * initialised UST can be used to process different messages while relying on * that UST to generate and use a different index value for each message without * the need for specifying eveytime that value; i.e. the implementation shall * keep track of the index to ensure no previously used value, generated and * used with the same key material, is ever re-used. Furthermore, because we * want to <em>maximise</em> the possible length of messages processed by a UST * with a given <em>key material</em>, each new/updated index value should yield * two keystream generators: one for use with the <em>Integrity Protection * service</em> (referred to as the Authentication Function in the UST draft), * and the other for use to encipher the message (<em>Confidentiality Protection * service</em>) --see Figures 1 and 2 in the UST draft. This way the maximum * plaintext protected by such a UST is <code>(MAX_KEYSTREAM_LENGTH - TAG_LENGTH) * </code> only, and the number of different messages protected by one set of * <em>key material</em> is <code>(2^(8*INDEX_LENGTH) - 1)</code> --the index * value <code>0</code> being reserved to generate all other keying material! To * achieve this objective this implementation does the following:</p> * * <ol> * <li>We define <code>MAX_INDEX</code> to be <code>(2^(8*INDEX_LENGTH) - 1) * </code> --all length quantities are expressed in bytes.</li> * <li>With every initialisation of a UST, a <em>keying</em> index of value * <code>0</code> and the user-supplied key material are used to initialise * an internal keystream generator. A <em>message</em> index is initialised * to <code>-1</code>.</li> * <li>With every start-of-message, (a) increment the <em>message</em> index, * and (b) check if it is greater than <code>MAX_INDEX</code>. If it is we * throw an exception. Otherwise, we generate from the <em>internal</em> * keystream enough key material to initialise two keystream generators, both * with that same index, one for the <em>Integrity Protection Function</em> * and the other for the <em>Confidentiality Protection Function</em>. Please * note that strictly speaking, the first of this pair of keystreams is NOT * the Confidentiality Protection Function keystream generator, since it is * also used to generate the bytes of the Integrity Protection Function's * <em>Prefix</em>. In other words, as soon as we detect the requirement for * Integrity Protection service, we instantiate the keystream generator * pair, even if the Confidentiality Protection service is not required.</li> * <li>If on the other hand, the start-of-message includes a designated index * value, we use that index as is, in combination with enough key material * generated by our internal keystream.</li> * <li>With every process-message (be it autheticate or encipher) we process * the message through one or both functions as desired (integrity protection * only, or integrity protection as well as confidentiality protection).</li> * <li>With every end-of-message, we terminate the integrity protection and * compute the authentication tag.</li> * <li>It is easy to show that it is possible to re-construct the state of * such a UST, given the user-supplied key material and a designated index * value.</li> * <li>The new constraint that would limit the number of messages, and their * length, with this scheme becomes: * <pre> * MAX_INDEX * 2 * (BLOCK_SIZE - INDEX_LENGTH) <= MAX_KEYSTREAM_LENGTH * </pre> * where <code>BLOCK_SIZE</code> is the underlying cipher's block size in * bytes.</li> * </ol> * * <p>Another alternative to using two keystream generators, would have been to * (a) use only one, but (b) in-line the code of the Authentication Function * inside the UST. This way, (a) enough output bytes from that keystream would * be used to setup the Authentication Function, and (b) the same output bytes * from that keystream would be used to compute the authentication context, as * well as encipher the message. But this weakens the UST, since an attacker, * with a known plaintext, can reconstruct the keystream generator output (used * for both the authentication and encipherement functions) by simply xoring the * message with the ciphertext.</p> * * <p>The next figure depicts a block diagram of our implementation of the UST: * </p> * * <pre> +----------------------------+ +-----------+ | user-supplied key material +------+ +-----+ Index = 0 | +----------------------------+ | | +-----------+ V V +-----------+ +------------------------------+ | Index | | Internal Keystream Generator | +-----+-----+ +-------------+----------------+ | | +----|------------------------------+ | | +-----------------+ | +--->| Confidentiality | +----|--->| Protection +--+-----------------+ | | | Generator | | | | | +-----------------+ V V | | +------------+---------------------------+ | | +------------+ Prefix | Suffix +---+ | | | +------------+---------------------------+ | | | | <------------- message -------------> | | | | +-------+---------------------------+ | | | | +-------+ Clear | Opaque +--(+) | | | | +-------+---------------------------+ | | | | | +---------------------------+ | | | | | +--------+ Ciphertext |<--+ | | | | | +---------------------------+ | | | V V | | | +-----------------+ | | +--->| Integrity | +-----------+ | +-------->| Protection +--->| Auth. Tag | +------------->| Generator | +-----------+ +-----------------+ * </pre> * * <p>References:</p> * * <ol> * <li><a * href="http://www.ietf.org/internet-drafts/draft-mcgrew-saag-ust-00.txt"> * The Universal Security Transform</a>, David A. McGrew.</li> * </ol> * * @version $Revision: 1.5 $ */ public class UST { // Constants and variables // ------------------------------------------------------------------------- /** Property name of a UST index_length. */ public static final String INDEX_LENGTH = "gnu.crypto.ust.index.length"; /** Property name of the keystream generator type to use. */ public static final String KEYSTREAM = "gnu.crypto.ust.keystream.name"; /** Property name of the keystream underlying cipher. */ public static final String CIPHER = "gnu.crypto.ust.cipher.name"; /** Property name of the UST user-supplied key material. */ public static final String KEY_MATERIAL = "gnu.crypto.ust.key"; /** Property name of the authentication tag length in bytes. */ public static final String TAG_LENGTH = "gnu.crypto.ust.tag.length"; /** Property name of the confidentiality protection flag. */ public static final String CONFIDENTIALITY = "gnu.crypto.ust.confidentiality"; /** Property name of the integrity protection flag. */ public static final String INTEGRITY = "gnu.crypto.ust.integrity"; /** caches the result of the correctness test, once executed. */ private static Boolean valid; /** Internal keystream generator. */ private IRandom keystream = null; /** Confidentiality protection function keystream generator. */ private IRandom cpStream = null; /** Integrity protection function keystream generator. */ private IRandom ipStream = null; /** Integrity protection function algorithm. */ private TMMH16 mac = null; /** Partially filled attributes of the internal keystream generator. */ private HashMap kAttributes = new HashMap(5); /** Partially filled attributes of the underlying confidentiality KSG. */ private HashMap cpAttributes = new HashMap(5); /** Partially filled attributes of the underlying integrity KSG. */ private HashMap ipAttributes = new HashMap(5); /** Partially filled attributes of the underlying Authentication Function. */ private HashMap macAttributes = new HashMap(2); /** Whether this UST should provide, or not, integrity protection. */ private boolean wantIntegrity = true; /** Whether this UST should provide, or not, confidentiality protection. */ private boolean wantConfidentiality = false; /** Size of user-supplied key material; same as internal keying material. */ private int keysize; /** The message index. */ private BigInteger index; /** The maximum value allowed for the index. */ private BigInteger maxIndex; /** Integrity protection function output length. */ private int macLength; /** Have we started? */ private boolean ready = false; /** The instance lock. */ private Object lock = new Object(); // Constructor(s) // ------------------------------------------------------------------------- // Class methods // ------------------------------------------------------------------------- // Instance methods // ------------------------------------------------------------------------- /** * <p>Initialise this instance with the designated set of attributes.</p> * * <p>The possible attributes for a <code>UST</code> are:</p> * * <ul> * <li>{@link #CONFIDENTIALITY}: a {@link java.lang.Boolean} that * indicates if Confidentiality Protection service is to be activated for * messages processed with this instance.</li> * <li>{@link #INTEGRITY}: a {@link java.lang.Boolean} that indicates if * Integrity Protection service is to be activated for messages processed * with this instance.</li> * <li>{@link #KEYSTREAM}: a {@link java.lang.String} that indicates the * algorithm name of the underlying keystream generators used with this * instance. Currently the only allowed values are: * {@link Registry#UMAC_PRNG} and {@link Registry#ICM_PRNG}.</li> * <li>{@link #INDEX_LENGTH}: a {@link java.lang.Integer} that is only * needed if the {@link Registry#ICM_PRNG} is chosen as the keystream * generator algorithm. This value is the count in bytes of the segment * index portion of an <code>ICMGenerator</code>. * <li>{@link #CIPHER}: a {@link java.lang.String} that indicates the * algorithm name of the underlying symmetric key block cipher to use with * the designated keystream generators. If this value is undefined, then * the default cipher algorithm for the selected keystream generator * algorithm shall be used (which is {@link Registry#RIJNDAEL_CIPHER} for * both keystream generator algorithms).</li> * <li>{@link IBlockCipher#CIPHER_BLOCK_SIZE}: a {@link java.lang.Integer} * that indicates the block-size to use with the designated symmetric key * block cipher algorithm. If this value is undefined, then the default * block size for the chosen cipher is used.</li> * <li>{@link #TAG_LENGTH}: a {@link java.lang.Integer} that indicates the * length of the resulting authentication tag, if/when the Integrity * Protection service is activated.</li> * <li>{@link #KEY_MATERIAL}: a byte array containing the user-supplied * key material needed to seed the internal keystream generator.</li> * </ul> * * @param attributes the map of attributes to use for this instance. */ public void init(Map attributes) { synchronized(lock) { String keystreamName = (String) attributes.get(KEYSTREAM); if (keystreamName == null) { throw new IllegalArgumentException(KEYSTREAM); } keystream = PRNGFactory.getInstance(keystreamName); kAttributes.clear(); // prepare for new values cpAttributes.clear(); ipAttributes.clear(); // find out which cipher algorithm to use String underlyingCipher = (String) attributes.get(CIPHER); if (underlyingCipher != null) { cpAttributes.put(ICMGenerator.CIPHER, underlyingCipher); ipAttributes.put(ICMGenerator.CIPHER, underlyingCipher); if (keystream instanceof ICMGenerator) { kAttributes.put(ICMGenerator.CIPHER, underlyingCipher); } else if (keystream instanceof UMacGenerator) { kAttributes.put(UMacGenerator.CIPHER, underlyingCipher); } else { throw new IllegalArgumentException(KEYSTREAM); } } // did she specify which block size to use it in? Integer blockSize = (Integer) attributes.get(IBlockCipher.CIPHER_BLOCK_SIZE); if (blockSize != null) { kAttributes.put(IBlockCipher.CIPHER_BLOCK_SIZE, blockSize); cpAttributes.put(IBlockCipher.CIPHER_BLOCK_SIZE, blockSize); ipAttributes.put(IBlockCipher.CIPHER_BLOCK_SIZE, blockSize); } // get the key material. byte[] key = (byte[]) attributes.get(KEY_MATERIAL); if (key == null) { throw new IllegalArgumentException(KEY_MATERIAL); } keysize = key.length; if (keystream instanceof ICMGenerator) { // for an ICMGenerator-based UST, the key material is effectively // twice the underlying cipher's desired/needed key size: half of // these bytes is the key material per se for the cipher, and the // other half shall be used as the "offset" for the ICMGenerator. // ensure length is > 0 and is even int limit = key.length; if (limit < 2) { throw new IllegalArgumentException(KEY_MATERIAL); } else if ((limit & 0x01) != 0) { throw new IllegalArgumentException(KEY_MATERIAL); } limit /= 2; byte[] cipherKey = new byte[limit]; byte[] offset = new byte[limit]; System.arraycopy(key, 0, cipherKey, 0, limit); System.arraycopy(key, limit, offset, 0, limit); kAttributes.put(IBlockCipher.KEY_MATERIAL, cipherKey); kAttributes.put(ICMGenerator.OFFSET, offset); } else { // if we're here then it's a UMacGenerator and the key is used as is kAttributes.put(IBlockCipher.KEY_MATERIAL, key); } // get the index length. it only makes sense for the ICMGenerator // for the UMacGenerator it's always 1 Integer ndxLength = (Integer) attributes.get(INDEX_LENGTH); if (ndxLength != null) { if (keystream instanceof ICMGenerator) { kAttributes.put(ICMGenerator.SEGMENT_INDEX_LENGTH, ndxLength); // max index length is 2 ^ segment-length-in-bits - 1 maxIndex = BigInteger.valueOf(2L).pow(8*ndxLength.intValue()) .subtract(BigInteger.ONE); } else if (ndxLength.intValue() != 1) { throw new IllegalArgumentException(INDEX_LENGTH); } else { maxIndex = BigInteger.valueOf(255L); } } else if (keystream instanceof ICMGenerator) { // we need this value throw new IllegalArgumentException(INDEX_LENGTH); } else { maxIndex = BigInteger.valueOf(255L); } // the keystream with index 0 is our source for keying material // in this implementation we shall use index 0 to compute the key // material for the hash function if (keystream instanceof ICMGenerator) { kAttributes.put(ICMGenerator.SEGMENT_INDEX, BigInteger.ZERO); } else { kAttributes.put(UMacGenerator.INDEX, new Integer(0)); } // we have everything we need. init the internal keystream generator keystream.init(kAttributes); index = BigInteger.valueOf(-1L); // find out what security services to provide Boolean confidentiality = (Boolean) attributes.get(CONFIDENTIALITY); if (confidentiality == null) { // by default we dont provide it wantConfidentiality = false; } else { wantConfidentiality = confidentiality.booleanValue(); } if (wantConfidentiality) { cpStream = PRNGFactory.getInstance(keystreamName); } Boolean integrity = (Boolean) attributes.get(INTEGRITY); if (integrity == null) { // by default we do provide it wantIntegrity = true; } else { wantIntegrity = integrity.booleanValue(); } if (wantIntegrity) { // make sure we have the other generator to provide our prefix if (cpStream == null) { cpStream = PRNGFactory.getInstance(keystreamName); } ipStream = PRNGFactory.getInstance(keystreamName); // only when integrity protection service is desired do we look for // a tag length property Integer tagLength = (Integer) attributes.get(TAG_LENGTH); if (tagLength == null) { throw new IllegalArgumentException(TAG_LENGTH); } macAttributes.put(TMMH16.TAG_LENGTH, tagLength); macLength = tagLength.intValue(); } ready = false; } } /** * <p>Signals the start of a new message to process with this <code>UST</code>. * </p> * * @return a byte array containing the representation of the <em>Index</em> * used for this message. * @throws LimitReachedException if the value of the <em>Index</em> has * reached its allowed upper bound. To use this <code>UST</code> instance * a new initialisation (with a new user-supplied key material) should occur. * @throws InvalidKeyException if the underlying cipher, used in either or * both the Integrity Protection and Confidentiality Protection functions * has detected an exception. */ public byte[] beginMessage() throws LimitReachedException, InvalidKeyException { beginMessageWithIndex(index.add(BigInteger.ONE)); return index.toByteArray(); } /** * <p>Signals the start of a new message to process with this <code>UST</code> * with a designated <emIndex</em> value.</p> * * @param ndx the <em>Index</em> to use with the new message. * @throws LimitReachedException if the value of the <em>Index</em> has * reached its allowed upper bound. To use this <code>UST</code> instance * a new initialisation (with a new user-supplied key material) should occur. * @throws InvalidKeyException if the underlying cipher, used in either or * both the Integrity Protection and Confidentiality Protection functions * has detected an exception. */ public void beginMessageWithIndex(int ndx) throws LimitReachedException, InvalidKeyException { beginMessageWithIndex(ndx); } /** * <p>Signals the start of a new message to process with this <code>UST</code> * with a designated <emIndex</em> value.</p> * * @param ndx the <em>Index</em> to use with the new message. * @throws LimitReachedException if the value of the <em>Index</em> has * reached its allowed upper bound. To use this <code>UST</code> instance * a new initialisation (with a new user-supplied key material) should occur. * @throws InvalidKeyException if the underlying cipher, used in either or * both the Integrity Protection and Confidentiality Protection functions * has detected an exception. */ public void beginMessageWithIndex(BigInteger ndx) throws LimitReachedException, InvalidKeyException { if (ndx.compareTo(maxIndex) > 0) { throw new LimitReachedException(); } index = ndx; // depending on the desired services, get keying material from the // internal keystream generator and complete the relevant attributes. // the key size shall be the same size as the user-supplied key material. // remember we need the 1st generator if integrity is required even when // confidentiality is not if (wantConfidentiality || wantIntegrity) { byte[] cpKey = new byte[keysize]; keystream.nextBytes(cpKey, 0, keysize); cpAttributes.put(IBlockCipher.KEY_MATERIAL, cpKey); if (cpStream instanceof ICMGenerator) { cpAttributes.put(ICMGenerator.SEGMENT_INDEX, index); } else { cpAttributes.put(UMacGenerator.INDEX, new Integer(index.intValue())); } cpStream.init(cpAttributes); } if (wantIntegrity) { byte[] ipKey = new byte[keysize]; keystream.nextBytes(ipKey, 0, keysize); ipAttributes.put(IBlockCipher.KEY_MATERIAL, ipKey); if (ipStream instanceof ICMGenerator) { ipAttributes.put(ICMGenerator.SEGMENT_INDEX, index); } else { ipAttributes.put(UMacGenerator.INDEX, new Integer(index.intValue())); } ipStream.init(ipAttributes); // get prefix bytes byte[] prefix = new byte[macLength]; cpStream.nextBytes(prefix, 0, macLength); macAttributes.put(TMMH16.PREFIX, prefix); mac = new TMMH16(); macAttributes.put(TMMH16.KEYSTREAM, ipStream); mac.init(macAttributes); } ready = true; } /** * <p>Process the <em>Clear</em> part of the message.</p> * * @param in a byte array containing the <em>Clear</em> part of the message. * @param offset the starting index in <code>in</code> where the <em>Clear</em> * message bytes should be considered. * @param length the count of bytes in <code>in</code>, starting from * <code>offset</code> to consider. * @throws IllegalStateException if no start-of-message method has been * invoked earlier or the Integrity Protection service has not been activated. */ public void doClear(byte[] in, int offset, int length) { if (!ready) { throw new IllegalStateException(); } if (!wantIntegrity) { throw new IllegalStateException(); } mac.update(in, offset, length); } /** * <p>Process the <em>Opaque</em> part of the message.</p> * * @param in a byte array containing the <em>Clear</em> part of the message. * @param inOffset the starting index in <code>in</code> where the * <em>Opaque</em> message bytes should be considered. * @param length the count of bytes in <code>in</code>, starting from * <code>inOffset</code> to consider. * @param out the byte array where the enciphered opaque message should be * stored. * @param outOffset the starting offset in <code>out</code> where the * enciphered bytes should be stored. * @throws IllegalStateException if no start-of-message method has been * invoked earlier. * @throws LimitReachedException if one or both of the underlying keystream * generators have reached their limit. */ public void doOpaque(byte[] in, int inOffset, int length, byte[] out, int outOffset) throws LimitReachedException { if (!ready) { throw new IllegalStateException(); } if (wantIntegrity) { mac.update(in, inOffset, length); } if (wantConfidentiality) { byte[] suffix = new byte[length]; cpStream.nextBytes(suffix, 0, length); for (int i = 0; i < length; ) { out[outOffset++] = (byte)(in[inOffset++] ^ suffix[i++]); } } else { System.arraycopy(in, inOffset, out, outOffset, length); } } /** * <p>Signals the end of the message transformation.</p> * * @return the authentication tag bytes, if and when the Integrity Protection * service was specified; otherwise a 0-long byte array is returned. * @throws IllegalStateException if no start-of-message method has been * invoked earlier. */ public byte[] endMessage() { if (!ready) { throw new IllegalStateException(); } if (!wantIntegrity) { return new byte[0]; } byte[] result = mac.digest(); reset(); return result; } /** * <p>Reset this instance and prepare for processing a new message.</p> */ public void reset() { ready = false; if (wantIntegrity) { mac.reset(); } } public boolean selfTest() { if (valid == null) { try { // TODO: compute and test equality with one known vector UST ust = new UST(); Map attributes = new HashMap(); attributes.put(KEYSTREAM, Registry.UMAC_PRNG); attributes.put(TAG_LENGTH, new Integer(4)); attributes.put(KEY_MATERIAL, "abcdefghijklmnop".getBytes("ASCII")); attributes.put(CONFIDENTIALITY, Boolean.TRUE); attributes.put(INTEGRITY, Boolean.TRUE); ust.init(attributes); ust.beginMessage(); ust.doClear("Giambattista Bodoni".getBytes("ASCII"), 0, 19); byte[] out = new byte[17]; ust.doOpaque("Que du magnifique".getBytes("ASCII"), 0, 17, out, 0); // System.out.println("ust.opaque=0x"+Util.toString(out)); // 0x45BED162FED5591B74ACB0E5CA742295FF byte[] tag = ust.endMessage(); // System.out.println("ust.tag=0x"+Util.toString(tag)); // 0x3489BA84 valid = Boolean.TRUE; } catch (Exception x) { x.printStackTrace(System.err); valid = Boolean.FALSE; } } return valid.booleanValue(); } }