Package de.esoco.microsafe.model

Source Code of de.esoco.microsafe.model.EncryptableNode

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// MicroSafe source file
// Copyright (c) 2006 Elmar Sonnenschein / esoco GmbH
// Last Change: 28.09.2006 by eso
//
// MicroSafe 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.
//
// MicroSafe 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
// MicroSafe; if not, write to the Free Software Foundation, Inc., 59 Temple
// Place, Suite 330, Boston, MA   02111-1307   USA or use the contact information
// from the GNU website http://www.gnu.org
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
package de.esoco.microsafe.model;

import de.esoco.j2me.J2meLib;
import de.esoco.j2me.model.BasicHierarchyNode;
import de.esoco.j2me.model.HierarchyNode;
import de.esoco.j2me.model.NodeAccessException;
import de.esoco.j2me.resource.ResourceBundle;
import de.esoco.j2me.storage.HierarchicalStorage;
import de.esoco.j2me.storage.StorageException;
import de.esoco.j2me.util.Log;

import de.esoco.microsafe.crypto.CryptoHandler;

import java.io.IOException;

import org.bouncycastle.crypto.CryptoException;


/********************************************************************
* Subclass of BasicHierarchyNode with extension to handle encryption of nodes
* (and node hierarchies). A hierarchy of EncryptedNodes can only contain
* instances of this class (or subclasses) because of the requirements for the
* encryption handling.
*
* <p>There must also be a special instance as the root node that implements the
* management of the CryptoHandler instance needed for encryption through the
* methods set/getCryptoHander(). This is necessary because the default
* implementation simply returns the result of rParent.set/getCryptoHandler().
* </p>
*
* @author eso
* @see    de.esoco.microsafe.crypto.CryptoHandler
*/
public abstract class EncryptableNode extends BasicHierarchyNode
{
  //~ Static fields/initializers ---------------------------------------------

  private static final char ENCRYPTION_NONE = 0;

  //~ Instance fields --------------------------------------------------------

  private char cEncryption = ENCRYPTION_NONE;

  //~ Constructors -----------------------------------------------------------

  /***************************************
   * Constructor, simply calls the same super constructor.
   *
   * @see de.esoco.j2me.model.BasicHierarchyNode
   */
  public EncryptableNode(EncryptableNode rParent, String sTitle, byte[] rData)
  {
    super(rParent, sTitle, rData);
  }

  //~ Methods ----------------------------------------------------------------

  /***************************************
   * To return the CryptoHandler for this node. It will be queried recursively
   * from the EncryptableNode implementation which is the root of the node
   * hierarchy (and which must therefore overload this method to return a
   * CryptoHander instance).
   *
   * @return A CryptoHandler instance or NULL if none is available
   */
  public CryptoHandler getCryptoHandler()
  {
    if (getParent() != null)
    {
      return ((EncryptableNode) getParent()).getCryptoHandler();
    }
    else
    {
      return null;
    }
  }

  /***************************************
   * Overloaded to decrypt the data before returning it if the node is
   * encrypted. This requires that a CryptoHandler instance has been set
   * before. Otherwise the data cannot be decrypted and a default value will
   * be returned.
   *
   * @return The data of the node, decrypted if the node is encrypted
   */
  public byte[] getData()
  {
    if (isEncrypted())
    {
      try
      {
        return getCryptoHandler().decrypt(aData);
      }
      catch (Exception e)
      {
        if (J2meLib.LOGGING)
        {
          Log.error(e);
        }

        return ResourceBundle.getCurrent().getString("MS_ErrDecrpt")
                   .getBytes();
      }
    }
    else
    {
      return super.getData();
    }
  }

  /***************************************
   * Returns the total number of encrypted child nodes in the hierarchy of
   * this node (not counting the starting node itself).
   *
   * @return The total number of children of the node
   */
  public int getTotalEncryptedChildCount()
  {
    int nCount = 0;

    for (int i = 0, n = getChildCount(); i < n; i++)
    {
      HierarchyNode c = getChild(i);

      if (c instanceof EncryptableNode)
      {
        EncryptableNode rChild = (EncryptableNode) c;

        if (rChild.isEncrypted())
        {
          nCount++;
        }

        nCount += rChild.getTotalEncryptedChildCount();
      }
    }

    return nCount;
  }

  /***************************************
   * Overloaded to handle encryption for the inserted nodes. If the child node
   * is encrypted differently than the current node the child node will be
   * re-encrypted with the new encryption. For this to work it is necessary
   * that the inserted node has still a reference to it's previous parent
   * node.
   *
   * <p>Regardless of the encryption state of the child node, if this node is
   * encrypted the child node and all it's children will be encrypted too by
   * this method.</p>
   *
   * @param  rChild The root node of the node hierarchy to be inserted
   * @param  nIndex The position in the child list where to insert the node
   *
   * @throws StorageException If updating the storage with the child data
   *                          fails
   *
   * @see    de.esoco.j2me.model.MutableHierarchyNode#insertChild(de.esoco.j2me.model.HierarchyNode,
   *         int)
   */
  public void insertChild(HierarchyNode rChild, int nIndex)
    throws NodeAccessException, StorageException
  {
    super.insertChild(rChild, nIndex);

    if (rChild instanceof EncryptableNode)
    {
      try
      {
        ((EncryptableNode) rChild).changeEncryption(getCryptoHandler());

        if (isEncrypted())
        {
          ((EncryptableNode) rChild).setEncryption(true);
        }
      }
      catch (CryptoException e)
      {
        throw new NodeAccessException(e);
      }
    }
  }

  /***************************************
   * To check if a node is encrypted.
   *
   * @return TRUE, if the node is encrypted
   */
  public boolean isEncrypted()
  {
    return (cEncryption != ENCRYPTION_NONE);
  }

  /***************************************
   * To enable or disable encryption for the node and all of it's children. If
   * no CryptoHandler is available through the method getCryptoHandler()
   * nothing will be changed by this method. That means that the application
   * is responsible to provide the cryptography setup.
   *
   * @param  bEncrypt If TRUE the node hierarchy will be encrypted; if FALSE
   *                  it will be decrypted
   *
   * @throws StorageException If updating the model data in the storage fails
   * @throws CryptoException  If enabling or disabling the encryption fails
   */
  public void setEncryption(boolean bEncrypt) throws CryptoException,
                             StorageException
  {
    CryptoHandler aCrypto = getCryptoHandler();

    if (aCrypto != null)
    {
      if (bEncrypt)
      {
        encrypt(aCrypto, false);
      }
      else
      {
        decrypt(aCrypto, false);
      }
    }
  }

  /***************************************
   * Re-encrypts the node and it's (encrypted) children from one encryption
   * handler to another. If the new encryption is same as the previous no
   * change will be made. This method will only re-encrypt nodes that are
   * already encrypted.
   *
   * @param  rNewCrypto The new CryptoHandler to apply
   *
   * @throws StorageException If updating the model data in the storage fails
   * @throws CryptoException  If changing the encryption fails
   */
  protected void changeEncryption(CryptoHandler rNewCrypto)
    throws CryptoException, StorageException
  {
    CryptoHandler rCurrentCrypto = getCryptoHandler();

    if (((rNewCrypto == null) && (rCurrentCrypto != null)) ||
      ((rNewCrypto != null) && !rNewCrypto.equals(rCurrentCrypto)))
    {
      // re-encryption is only necessary if nodes are already encrypted
      if (rCurrentCrypto != null)
      {
        // first decrypt either all if handler removed (NULL) or only
        // the already encrypted nodes if encryption handler has changed
        decrypt(rCurrentCrypto, (rNewCrypto != null));

        // then re-encrypt if handler changed
        if (rNewCrypto != null)
        {
          encrypt(rNewCrypto, true);
        }
      }
    }
  }

  /***************************************
   * Overloaded to copy the additional data fields of EncryptableNode (i.e.
   * the encryption flag).
   *
   * @param rTarget The target node for the copied data
   *
   * @see   BasicHierarchyNode#copyFields(BasicHierarchyNode)
   */
  protected void copyFields(BasicHierarchyNode rTarget)
  {
    super.copyFields(rTarget);

    ((EncryptableNode) rTarget).cEncryption = cEncryption;
  }

  /***************************************
   * Internal method to decrypt a node and all it's children with a certain
   * CryptoHandler instance. If the parameter bKeepFlag is TRUE, the node's
   * encryption flag will not be reset. This is used internally to propagate
   * key changes and must always be followed by invoking the method encrypt()
   * on the same starting node with the parameter bOnlyIfFlagSet set to TRUE!
   *
   * <p>If bKeepFlag is FALSE, all encrypted nodes in the hierarchy starting
   * with this node will be decrypted (assuming the CryptoHandler contains the
   * correct key) and marked as such.</p>
   *
   * @param  rCrypto   The CryptoHander to use
   * @param  bKeepFlag If TRUE, the encryption flag of the node is not reset
   *
   * @throws CryptoException  If decrypting the node fails
   * @throws StorageException If updating the storage fails
   */
  protected void decrypt(CryptoHandler rCrypto, boolean bKeepFlag)
    throws CryptoException, StorageException
  {
    if (isEncrypted())
    {
      if (aData != null)
      {
        aData = rCrypto.decrypt(aData);
      }

      // if bKeepFlag is TRUE this is just the first loop of a key change;
      // update of the storage will happen in the following call to encrypt()
      if (!bKeepFlag)
      {
        cEncryption = ENCRYPTION_NONE;
        updateStorage(getStorage());
      }
    }

    for (int i = 0, cnt = getChildCount(); i < cnt; i++)
    {
      ((EncryptableNode) getChild(i)).decrypt(rCrypto, bKeepFlag);
    }
  }

  /***************************************
   * Internal method to encrypt a node and all it's children with a certain
   * CryptoHandler instance. If the parameter bOnlyIfFlagSet is TRUE, only
   * nodes that have their encryption flag already set will be encrypted
   * (again). This is used internally to propagate key changes and must have
   * been preceded by a call to decrypt(CryptoHander, TRUE)!
   *
   * <p>If bOnlyIfFlagSet is FALSE, only nodes that don't have the flag set
   * will be encrypted and marked as such.</p>
   *
   * @param  rCrypto        The CryptoHander to use
   * @param  bOnlyIfFlagSet If TRUE, only the nodes already marked as
   *                        encrypted will be processed
   *
   * @throws CryptoException  If encrypting the node fails
   * @throws StorageException If updating the storage fails
   */
  protected void encrypt(CryptoHandler rCrypto, boolean bOnlyIfFlagSet)
    throws CryptoException, StorageException
  {
    boolean bEnc = isEncrypted();

    if ((bOnlyIfFlagSet && bEnc) || (!bOnlyIfFlagSet && !bEnc))
    {
      // TODO: check if title should be encrypted too
      if (aData != null)
      {
        aData = rCrypto.encrypt(aData);
      }

      cEncryption = CryptoHandler.getEncryptionType();
      updateStorage(getStorage());
    }

    for (int i = 0, cnt = getChildCount(); i < cnt; i++)
    {
      // recursively encrypt all children
      ((EncryptableNode) getChild(i)).encrypt(rCrypto, bOnlyIfFlagSet);
    }
  }

  /***************************************
   * Overloaded to load additional node fields (currently three reserved byte
   * values).
   *
   * @see BasicHierarchyNode#readFields(HierarchicalStorage)
   */
  protected int readFields(HierarchicalStorage rStorage)
    throws StorageException, IOException
  {
    int nResult = super.readFields(rStorage);

    cEncryption = (char) rStorage.readByte();

    return nResult;
  }

  /***************************************
   * Overloaded to perform encryption before storing the data if the node is
   * encrypted. This requires that a CryptoHandler instance has been set
   * before. Otherwise the data cannot be encrypted and will remain unchanged.
   *
   * @param rNewData
   *
   * @see   BasicHierarchyNode#setDataField(byte[])
   */
  protected void setDataField(byte[] rNewData)
  {
    if (isEncrypted())
    {
      try
      {
        aData = getCryptoHandler().encrypt(rNewData);
      }
      catch (Exception e)
      {
        if (J2meLib.LOGGING)
        {
          Log.error(e);
        }
      }
    }
    else
    {
      super.setDataField(rNewData);
    }
  }

  /***************************************
   * Overloaded to write additional node fields (currently three reserved byte
   * values).
   *
   * @see BasicHierarchyNode#writeFields(HierarchicalStorage)
   */
  protected void writeFields(HierarchicalStorage rStorage)
    throws StorageException, IOException
  {
    super.writeFields(rStorage);
    rStorage.writeByte((byte) cEncryption);
  }
}
TOP

Related Classes of de.esoco.microsafe.model.EncryptableNode

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.