[crypto] OAEP AONT (gnu/javax/crypto/mode/OAE.java)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




dear reader,

attached is an OAEP AONT, ready for your comments or cvs



cheers,

John



-------------- next part --------------
/* AEP.java -- 
   Copyright (C) 2001, 2002, 2003, 2006 Free Software Foundation, Inc.

This file is a part of GNU Classpath.

GNU Classpath 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 of the License, or (at
your option) any later version.

GNU Classpath 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 GNU Classpath; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, 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.  */


package gnu.javax.crypto.mode;


/**
 * <p> An <i>All Or Nothing Transform</i> (AONT) mode using <i>Optimal
 * Asymmetric Encryption Padding</i> (OAEP).  OAEP was created by
 * Mihir Bellare and Phillip Rogaway in their paper "Optimal
 * Asymmetric Encryption" of November 1995.  This software implements
 * the basic scheme described on page eight (8) of that paper, and
 * analyzed by Rivest's student Victor Boyko in his thesis (see
 * references). </p>
 *
 *
 * <h3><a name="implementation">Implementation Notes</a></h3>
 * 
 * <ul>
 * 
 * <li> This class is implemented as a transform on application (total
 * crypto chain) plaintext.  The OAEP AONT is applied before
 * encryption and after decryption. </li>
 * 
 * <li> The AONT is applied to blocks, each block an independent OAEP
 * unit.  </li>
 * 
 * <li> The mode block size is distinct from the cipher block size, as
 * OAEP includes an additional hash- digest- size number of bytes in
 * its forward output.  Mode and cipher block sizes are configured and
 * corrected as necessary during mode initialization.  An invalid
 * cipher block size is reset to a cipher block size that produces an
 * OAEP overhead of two or better (less than two). </li>
 * 
 * <li> As in {@link BaseMode}, the method {@link
 * gnu.javax.crypto.cipher.IBlockCipher#defaultBlockSize()} returns
 * the cipher block size, and the method {@link
 * gnu.javax.crypto.cipher.IBlockCipher#currentBlockSize()} returns
 * the mode block size. </li>
 * 
 * <li> The selection of mode and cipher block sizes has been worked
 * out to be practical in communication for interoperability.  See
 * {@link #blockSizes()} and {@link #init(java.util.Map)}. </li>
 * 
 * <li> Instances of this class are stateless with respect to
 * direction of algorithmic operation.  The state of this class is in
 * the configuration of its three processor functions (H, R, and G),
 * each of which are effectively stateless with respect to algorithmic
 * processing in foward and inverse directions.  </li>
 * 
 * </ul>
 * 
 * 
 * <h3><a name="application">Application Notes</a></h3>
 * 
 * <ul>
 * 
 * <li> The OAEP random seed function (R) does not need to be
 * deterministic (not necessarily a pseudo random sequence), and
 * ideally gets bits from a truly random source.  </li>
 * 
 * <li> The OAEP random seed function (R) needs to be able to produce
 * <code>hash-size * mode-input-block-count</code> bytes without
 * exhaustion. </li>
 * 
 * <li> The OAEP hash function (H) is irreversible (ie, it is a secure
 * hash function). </li>
 * 
 * <li> Refer to Bellare and Rogaway "Optimal Asymmetric Encryption"
 * for information on the required properties of the generator
 * function (G).  This function is deterministic with respect to its
 * input (R).  </li>
 * 
 * <li> The OAEP/AONT output under encryption includes the overhead of
 * bytes appended in the size of the hash digest.  The mode block size
 * is the cipher block size minus the hash digest size, or
 * equivalently the cipher block size is the mode block size plus the
 * hash digest size.  OAEP/AONT overhead is the ratio of cipher block
 * size to hash digest size.  For example, when the cipher block size
 * and hash digest size are identical, then the ciphertext will be
 * twice the size of the plaintext for 0% cipher overhead with 100%
 * OAEP overhead on the plaintext.  </li>
 * 
 * <li> The "OAEP" algorithm is distinct from and incompatible with
 * the "EME-OAEP" used for "RSA padding". </li>
 * 
 * </ul>
 * 
 * 
 * <h3><a name="initialization">Initialization options</a></h3>
 * 
 * <p> This class employs three function objects that may be
 * configured via its setup parameters map.  Defaults are provided as
 * documented.
 * 
 * <dl>
 *
 * <dt><a name="InitHash">Hash Function (H)</a></dt> <dd> The init
 * parameter {@link #INIT_H} may have a string value to name a hash
 * function (instance of {@link
 * gnu.java.security.hash.IMessageDigest}) as returned by {@link
 * gnu.java.security.hash.HashFactory#getInstance(java.lang.String)}.
 * Refer to the <code>*_HASH</code> constants defined in {@link
 * gnu.java.security.Registry}.  The default hash function is defined
 * by {@link #INIT_H_DEFAULT}.  </dd>
 * 
 * <dt><a name="InitRand">Random Seed Function (R)</a></dt> <dd> The
 * init parameter {@link #INIT_R} may have an object value instance of
 * {@link gnu.java.security.prng.IRandom} which is presumed to be
 * initialized when found.  The rand function object is presumed to be
 * initialized to permit the case of sharing systemic entropy pools.
 * The default random seed function is defined by {@link
 * #INIT_R_DEFAULT}.  </dd>
 * 
 * <dt><a name="InitGenerator">Generator Function (G)</a></dt> <dd>
 * The init parameter {@link #INIT_G} may have an object value
 * subclass of the stateless {@link OAE$Generator}.  The default
 * generator function is {@link OAE$Generator}.  </dd>
 * 
 * </dl>
 * </p>
 * 
 * 
 * <h3><a name="background">OAEP as AONT</a></h3>
 * 
 * <p> OAEP transforms the text for an "all or nothing" property: an
 * OAEP `ciphertext' decodes to its proper plaintext using only the
 * information in the ciphertext (no key), but only with 100% of the
 * ciphertext.  A partial ciphertext has zero value to analysis. </p>
 * 
 * <p> Victor Boyko (MIT) wrote a nice paper in this perspective in
 * August 1999: "On the Security Properties of OAEP as an All- Or-
 * Nothing Transform".  The "AONT" was first proposed by Ron Rivest
 * (MIT) in "All- Or- Nothing Encryption and The Package Transform".
 * Boyko's work has shown that OAEP is provably secure in the random
 * oracle model for two random oracles: a generator (G), and a hash
 * function (H). </p>
 * 
 * <p> An AONT like OAEP is an excellent encryption preprocessor for
 * ciphers with plaintext weaknesses, especially Vernam (One Time Pad
 * or XOR) ciphers.  With an AONT under the cipher, the plaintext is
 * completely obscured from analysis. </p>
 * 
 * <p> The intented use of this software is for AONT applications: it
 * is not the plaintext aware OAEP (guarantees that a ciphertext can
 * only be produced from a particular plaintext) with padding.  For
 * example, an AONT is valuable where the provable weakness of an
 * encryption algorithm is in partial plaintexts -- as in Vernam
 * streams like RC4.  The AONT encoding of the plaintext ensures that
 * a cipher's partial plaintext (the AONT ciphertext) remains a
 * completely secure message (AONT plaintext).  Refer to the AONT
 * papers by Boyko ("Security Properties of OAEP") and Rivest
 * ("Package Transform") for more information. </p>
 * 
 * 
 * <h3><a name="references">References</a></h3>
 * 
 * <dl>
 * 
 * <dt>Optimal Asymmetric Encryption</dt>
 * <dd>Mihir Bellare and Phillip Rogaway, November 1995</dd> 
 * 
 * <dt>On the Security Properties of OAEP as an All- Or-
 * Nothing Transform</dt>
 * <dd>Victor Boyko, August 1999</dd> 
 * 
 * <dt>All- Or- Nothing Encryption and The Package Transform</dt>
 * <dd>Ron Rivest</dd> 
 * 
 * </dl>
 *
 * @author John Pritchard (jdp@xxxxxxxxxxxx)
 * 
 */
public final class OAE
    extends BaseMode 
{

    // Constants and variables
    // -------------------------------------------------------------------------

    /**
     * <p> Mode name is "OAE" --- three characters in keeping with
     * structured mode strings format. </p>
     */
    public final static java.lang.String NAME = "OAE";
    /**
     * <p> Init attributes parameter name for a hash function name
     * string value for the hash factory. </p>
     * 
     * @see gnu.java.security.Registry
     * @see gnu.java.security.hash.HashFactory
     * @see #INIT_H_DEFAULT
     * @see #InitHash
     */
    public final static java.lang.String INIT_H = "gnu.javax.crypto.mode.OAE.name-hash";
    /**
     * <p> Default hash function (name) is MD5. </p>
     * 
     * @see gnu.java.security.Registry#MD5_HASH
     * @see gnu.java.security.hash.HashFactory
     * @see #INIT_H
     * @see #InitHash
     */
    public final static java.lang.String INIT_H_DEFAULT = gnu.java.security.Registry.MD5_HASH;
    /**
     * <p> Init attributes parameter name for a rand function object
     * value.  The rand function object is presumed to be initialized
     * to permit the case of sharing systemic entropy pools. </p>
     * 
     * @see #INIT_R_DEFAULT
     * @see #InitRand
     */
    public final static java.lang.String INIT_R = "gnu.javax.crypto.mode.OAE.object-rand";
    /**
     * <p> Default rand function (R) is the {@link
     * gnu.java.security.prng.MDGenerator} returned from {@link
     * gnu.java.security.prng.PRNGFactory#getInstance(java.lang.String)}
     * using {@link gnu.java.security.Registry#MD_PRNG} and then
     * initialized with {@link
     * gnu.java.security.prng.MDGenerator.MD_NAME} value {@link
     * gnu.java.security.Registry.SHA160_HASH} (the value of this
     * constant). </p>
     * 
     * @see #INIT_R
     * @see #InitRand
     */
    public final static java.lang.String INIT_R_DEFAULT = gnu.java.security.Registry.SHA160_HASH;
    /**
     * <p> Init attributes parameter name for a generator function
     * object value, subclass of {@link OAE$Generator}.  The default
     * generator is {@link OAE$Generator}. </p>
     * 
     * @see #InitGenerator
     */
    public final static java.lang.String INIT_G = "gnu.javax.crypto.mode.OAE.object-generator";


    private gnu.java.security.hash.IMessageDigest hash;

    private int hash_len;

    private gnu.java.security.prng.IRandom rand;

    private OAE.Generator generator;

    // Constructor(s)
    // -------------------------------------------------------------------------

    /**
     * @param cipher The underlying cipher
     * @param cbs Cipher block size, in bytes, in which to operate the
     * underlying cipher.
     */
    public OAE(gnu.javax.crypto.cipher.IBlockCipher cipher, int cbs)
    {
	super(NAME,cipher,cbs);
    }
    /**
     * Copy constructor used by clone method
     */
    private OAE(OAE copy){
	this((gnu.javax.crypto.cipher.IBlockCipher)copy.cipher.clone(),copy.cipherBlockSize);
    }

    // Class methods
    // -------------------------------------------------------------------------

    // Instance methods
    // -------------------------------------------------------------------------

    // IBlockCipher interface implementation ------------------------------------------

    /**
     * <p> For correctness of operation, this method returns the same
     * set of mode block sizes under both encryption and decryption
     * directions.  This symmetry permits the configuration stage of
     * intialization to consistently choose correct block sizes in
     * both directions (for decoding the encoding of this block size
     * algorithm). </p>
     * 
     * @return Mode block sizes filtered to overhead of two or better
     * that correspond to valid cipher block sizes under forward and
     * inverse directions.
     */ 
    public java.util.Iterator blockSizes(){
	if (null == this.hash)
	    throw new java.lang.IllegalStateException("Uninitialized");
	else {
	    java.util.Iterator cipher = this.cipher.blockSizes();
	    java.util.ArrayList list = new java.util.ArrayList();
	    int mode_block, cipher_block, hash_len = this.hash_len;
	    while (cipher.hasNext()){
		cipher_block = ((java.lang.Integer)cipher.next()).intValue();
		/*
		 * A symmetric block size selection algorithm is
		 * restricted by the encryption case.
		 */
		mode_block = (cipher_block-hash_len);
		//
		if (hash_len <= mode_block)/* Only 
					    *    (0 < mode_block), 
					    * is necessary, but reasonable overhead
					    * of two or better results from
					    *    (hash_len <= mode_block).
					    */
		    list.add(new java.lang.Integer(mode_block));
	    }
	    return java.util.Collections.unmodifiableList(list).iterator();
	}
    }

    // IMode interface implementation ------------------------------------------

    /**
     * <p> Calls super init and then configures hash, rand and
     * generator functions.  Defines mode and cipher block sizes as
     * necessary.  </p>
     * @see #INIT_H
     * @see #INIT_R
     * @see #INIT_G
     */
    public void init(java.util.Map attributes) 
	throws java.security.InvalidKeyException,
	       java.lang.IllegalStateException
    {
	synchronized(this.lock){
	    super.init(attributes);/*(calls setup, nop)
				    */
	    java.lang.String name;
	    /*
	     * Init hash function
	     */
	    name = (java.lang.String)attributes.get(INIT_H);
	    if (null == name)
		name = INIT_H_DEFAULT;
	    this.hash = gnu.java.security.hash.HashFactory.getInstance(name);
	    this.hash_len = this.hash.hashSize();
	    /*
	     * Define mode block size in a consistent way for decoding
	     * our own encodings, and in a readily communicable way
	     * for potential interoperability.
	     */
	    {
		int block_cipher = this.cipherBlockSize;
		int block_mode = (block_cipher - this.hash_len);
		if (0 < block_mode)
		    /*
		     * Liberal acceptance for deterministic cipher block sizes 
		     */
		    this.modeBlockSize = block_mode;
		else {
		    java.util.Iterator blocks = this.blockSizes();
		    if (blocks.hasNext()){
			while (blocks.hasNext()){
			    /*
			     * Heuristic block size determination
			     * for something larger rather than
			     * smaller, but not shooting for
			     * greater than 512 bytes for network
			     * throughput performance.
			     */
			    block_mode = ((java.lang.Integer)blocks.next()).intValue();
			    if (0xff < block_mode)
				break;
			}
			this.modeBlockSize = block_mode;
			/*
			 * Depends on impl of (this.blockSizes)
			 */
			this.cipherBlockSize = (block_mode + this.hash_len);
			/*
			 * Reinit cipher for block size selection
			 */
			attributes.put(MODE_BLOCK_SIZE,new java.lang.Integer(this.modeBlockSize));
			attributes.put(CIPHER_BLOCK_SIZE,new java.lang.Integer(this.cipherBlockSize));
			if (null == attributes.get(IV))
			    this.iv = new byte[this.modeBlockSize];//(for completeness)
			this.cipher.reset();
			this.cipher.init(attributes);
		    }
		    else
			throw new java.lang.IllegalStateException("Unable to identify a mode block size to accomodate hash '"+this.hash.name()+"' and cipher '"+this.cipher.name()+"'.");
		}
	    }
	    /*
	     * Init rand function
	     */
	    gnu.java.security.prng.IRandom rand;
	    rand = (gnu.java.security.prng.IRandom)attributes.get(INIT_R);
	    if (null == rand){
		name = INIT_R_DEFAULT;
		rand = gnu.java.security.prng.PRNGFactory.getInstance(gnu.java.security.Registry.MD_PRNG);
		java.util.Map params = new java.util.HashMap();
		params.put(gnu.java.security.prng.MDGenerator.MD_NAME,name);
		rand.init(params);
	    }
	    this.rand = rand;
	    /*
	     * Init generator function
	     */
	    OAE.Generator generator = (OAE.Generator)attributes.get(INIT_G);
	    if (null == generator)
		generator = new OAE.Generator();
	    this.generator = generator;
	}
    }

    // methods to be implemented by concrete subclasses ------------------------

    /**
     * @return Copy of this object as others in this package
     * guarantees a stateless clone type operation in a simple,
     * readable and maintainable way for final classes.  This clone
     * requires initialization.
     */
    public Object clone(){
	return new OAE(this);
    }
    /**
     * No op
     */
    public void setup(){
	/*
	 *(nop, see init)
	 */
    }
    /**
     * Releases references to the stateless generator, the independent
     * random seed function, and the private scope hash function.
     */
    public void teardown(){
	this.hash = null;
	this.generator = null;
	this.rand = null;
    }

    public void encryptBlock(byte[] in, int in_ofs, byte[] out, int out_ofs){
	try {
	    /*
	     * Output to cipher with structure 
	     * 
	     *     S           +          T
	     * 
	     *   (ofs 0)                (ofs mode-block-size)
	     *   (len mode-block-size)  (len hash-size)
	     * 
	     * N.B.  cipher-block-size = (mode-block-size + hash-size)
	     */
	    byte[] aont = new byte[this.cipherBlockSize];
	    //
	    byte[] R = new byte[this.hash_len];
	    this.rand.nextBytes(R,0,this.hash_len);
	    //
	    byte[] Gr = this.generator.generate(this.hash,R,this.modeBlockSize);
	    // S = (M xor G(r)) 
	    int idx = 0, aont_idx = 0, in_idx = in_ofs;
	    for (int S; (idx < this.modeBlockSize); in_idx++, idx++, aont_idx++){
		S = (in[in_idx] & 0xff) ^ (Gr[idx] & 0xff);
		aont[aont_idx] = (byte)S;
	    }
	    // T = (R xor H( X xor G(r)))
	    this.hash.update(aont,0,this.modeBlockSize);
	    byte[] Hx = this.hash.digest();
	    idx = 0;
	    for (int T; (idx < this.hash_len); idx++, aont_idx++){
		T = (R[idx] & 0xff) ^ (Hx[idx] & 0xff);
		aont[aont_idx] = (byte)T;
	    }
	    this.cipher.encryptBlock(aont,0,out,out_ofs);
	}
	catch (gnu.java.security.prng.LimitReachedException lre){
	    java.lang.RuntimeException rex = new java.lang.IllegalStateException("Rand function exhausted.");
	    rex.initCause(lre);
	    throw rex;
	}
    }

    public void decryptBlock(byte[] in, int in_ofs, byte[] out, int out_ofs){
	/*
	 * Input from cipher with structure 
	 * 
	 *     S           +          T
	 * 
	 *   (ofs 0)                (ofs mode-block-size)
	 *   (len mode-block-size)  (len hash-size)
	 * 
	 * N.B.  cipher-block-size = (mode-block-size + hash-size)
	 */
	byte[] aont = new byte[this.cipherBlockSize];
	//
	this.cipher.decryptBlock(in,in_ofs,aont,0);
	// H(S)
	this.hash.update(aont,0,this.modeBlockSize);
	byte[] Hs = this.hash.digest();
	// R = T xor H(s)
	byte[] R = new byte[this.hash_len];
	for (int idx = 0, r, T = this.modeBlockSize; (idx < this.hash_len); idx++, T++){
	    r = (aont[T] & 0xff) ^ (Hs[idx] & 0xff);
	    R[idx] = (byte)r;
	}
	byte[] Gr = this.generator.generate(this.hash,R,this.modeBlockSize);
	for (int M, idx = 0, out_idx = out_ofs; (idx < this.modeBlockSize); idx++, out_idx++){
	    M = (aont[idx] & 0xff) ^ (Gr[idx] & 0xff);
	    out[out_idx] = (byte)M;
	}
    }

    // own methods -------------------------------------------------------------

    /**
     * <p> Default OAEP Mask Generator (G) function may be subclassed.
     * This class is stateless, it has no fields.  The programming
     * interface presented here presumes that subclasses are likewise
     * stateless. </p>
     * 
     * @see OAE#InitGenerator
     * @see OAE#INIT_G
     */
    public static class Generator
	extends java.lang.Object
    {
	protected Generator(){
	    super();
	}
	
	/**
	 * The stateless OAEP generator function produces a One Time
	 * Pad (OAEP Mask) given a random seed.
	 * @param user Access configured functions
	 * @param R Random seed, offset zero for array length
	 * @param out_len Required pad material, return array length
	 * @return Pad material derived from seed in offset zero for
	 * <code>out_len<code> bytes.
	 */
	public byte[] generate(gnu.java.security.hash.IMessageDigest hash, byte[] R, int out_len){

	    int hash_len = hash.hashSize();
	    /*
	     * Herein lies the security of OAEP
	     */
	    int loopbound;
	    {
		double lo = out_len, lh = hash_len;

		loopbound = (int)(Math.ceil(lo/lh)); //=>strictfp StrictMath.ceil()
	    }

	    byte[] hbuf, mask = new byte[out_len], intbits = new byte[4];

	    int maskofs = 0, R_len = R.length;

	    for ( int i = 0; i < loopbound; i++){

		hash.update(R,0,R_len);

		hash.update(Bits32(i,intbits));

		hbuf = hash.digest();

		if ( (maskofs+hash_len) < out_len)

		    System.arraycopy(hbuf,0,mask,maskofs,hash_len);

		else {
		    int copymany = out_len-maskofs;

		    if ( 0 < copymany)
			System.arraycopy(hbuf,0,mask,maskofs,copymany);

		    return mask;//
		}

		maskofs += hash_len;
	    }
	    return mask;//
	}

	/**
	 * Pack bits into buf in network (java) byte order
	 * @param bits 32 unsigned bits
	 * @param buf Four bytes
	 * @return buf
	 */
	protected final static byte[] Bits32(int bits, byte[] buf){
	    buf[0] = (byte)((bits >>> 24) & 0xff);
	    buf[1] = (byte)((bits >>> 16) & 0xff);
	    buf[2] = (byte)((bits >>>  8) & 0xff);
	    buf[3] = (byte)(bits & 0xff);
	    return buf;
	}
    }
}

[Index of Archives]     [Linux Kernel]     [Linux Cryptography]     [Fedora]     [Fedora Directory]     [Red Hat Development]

  Powered by Linux