Package ch.ethz.inf.vs.scandium.dtls

Source Code of ch.ethz.inf.vs.scandium.dtls.RawPublicKey

/*******************************************************************************
* Copyright (c) 2014, Institute for Pervasive Computing, ETH Zurich.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
*    may be used to endorse or promote products derived from this software
*    without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Scandium (Sc) Security for Californium.
******************************************************************************/
package ch.ethz.inf.vs.scandium.dtls;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;

import ch.ethz.inf.vs.scandium.util.DatagramReader;
import ch.ethz.inf.vs.scandium.util.DatagramWriter;

/**
* A raw public key only contains the SubjectPublicKeyInfo structure instead of
* the entire certificate. This usage of raw public keys, instead of X.509-based
* certificates, leads to a smaller code footprint. For details see <a
* href="http://tools.ietf.org/html/draft-ietf-tls-oob-pubkey-03">TLS
* Out-of-Band Public Key Validation</a> and <a
* href="http://tools.ietf.org/html/rfc5480">RFC 5480</a>.
*
* @author Stefan Jucker
*
*/
public class RawPublicKey {

  // Logging ///////////////////////////////////////////////////////////

  private static final Logger LOGGER = Logger.getLogger(RawPublicKey.class.getCanonicalName());

  // Tags ///////////////////////////////////////////////////////////

  private final static int BIT_STRING_TAG = 0x03;

  private final static int NULL_TAG = 0x05;

  private final static int OBJECT_IDENTIFIER_TAG = 0x06;

  private final static int SEQUENCE_TAG = 0x30;

  // Constants //////////////////////////////////////////////////////

  private static final int OCTET_BITS = 8;

  // Members ////////////////////////////////////////////////////////

  private int[] algorithmOID;

  private int[] parametersOID;

  private byte[] subjectPublicKey;

  // Constructors ///////////////////////////////////////////////////

  public RawPublicKey() {
    this.subjectPublicKey = null;
    this.algorithmOID = null;
    this.parametersOID = null;
  }

  public RawPublicKey(byte[] subjectPublicKey, int[] algorithmOID, int[] parametersOID) {
    this.subjectPublicKey = subjectPublicKey;
    this.algorithmOID = algorithmOID;
    this.parametersOID = parametersOID;
  }

  // Serialization //////////////////////////////////////////////////

  public byte[] toByteArray() {

    DatagramWriter writer = new DatagramWriter();

    // AlgorithmIdentifier ::= SEQUENCE {
    // algorithm OBJECT IDENTIFIER,
    // parameters ANY DEFINED BY algorithm OPTIONAL
    // }
    byte[] algorithmBytes = writeTLV(OBJECT_IDENTIFIER_TAG, encodeOID(algorithmOID));
    byte[] parametersBytes;
    if (parametersOID != null) {
      parametersBytes = writeTLV(OBJECT_IDENTIFIER_TAG, encodeOID(parametersOID));
    } else {
      parametersBytes = writeTLV(NULL_TAG, new byte[0]);
    }
    byte[] algorithmIdentifierBytes = writeTLV(SEQUENCE_TAG, ByteArrayUtils.concatenate(algorithmBytes, parametersBytes));

    // SubjectPublicKeyInfo ::= SEQUENCE {
    // algorithm AlgorithmIdentifier,
    // subjectPublicKey BIT STRING
    // }
    byte[] subjectPublicKeyBytes = writeTLV(BIT_STRING_TAG, subjectPublicKey);
    byte[] subjectPublicKeyInfo = writeTLV(SEQUENCE_TAG, ByteArrayUtils.concatenate(algorithmIdentifierBytes, subjectPublicKeyBytes));

    writer.writeBytes(subjectPublicKeyInfo);

    return writer.toByteArray();
  }

  public static RawPublicKey fromByteArray(byte[] byteArray) {

    RawPublicKey rawPublicKey = new RawPublicKey();
    readTLV(byteArray, rawPublicKey);

    return rawPublicKey;
  }

  // Methods ////////////////////////////////////////////////////////

  /**
   * Reads a part of the bytestream according to the specified DER tag and the
   * length.
   *
   * @param byteArray
   *            the encoded TLV triplet.
   * @param rawPublicKey
   *            the {@link RawPublicKey} which needs to be initialized.
   */
  private static void readTLV(byte[] byteArray, RawPublicKey rawPublicKey) {
    DatagramReader reader = new DatagramReader(byteArray);

    while (reader.bytesAvailable()) {
      int tag = reader.read(OCTET_BITS);
      int length = reader.read(OCTET_BITS);

      // decode the length of the value, if encoded into multiply bytes
      if (length > 127) {
        int additionalBytes = length & 0x7F;
        length = reader.read(additionalBytes * OCTET_BITS);
      }
      byte[] fragment = reader.readBytes(length);

      switch (tag) {
      case SEQUENCE_TAG:
        readTLV(fragment, rawPublicKey);
        break;

      case OBJECT_IDENTIFIER_TAG:
        int[] oid = decodeOID(fragment);
        rawPublicKey.setObjectIdentifier(oid);
        break;

      case BIT_STRING_TAG:
        // strip the first byte (unused counter)
        // http://msdn.microsoft.com/en-us/library/windows/desktop/bb540792(v=vs.85).aspx
        byte[] subjectPublicKey = new byte[fragment.length - 1];
        System.arraycopy(fragment, 1, subjectPublicKey, 0, fragment.length - 1);
        rawPublicKey.setBitString(subjectPublicKey);
        break;

      case NULL_TAG:
        // do nothing
        // http://msdn.microsoft.com/en-us/library/windows/desktop/bb540808(v=vs.85).aspx
        break;

      default:
        LOGGER.warning("Unknown DER tag: " + tag);
        break;
      }
    }
  }

  /**
   * Writes a TLV triplet (tag-length-value).
   *
   * @param tag
   *            the tag.
   * @param value
   *            the value to be written.
   * @return the corresponding byte array representation.
   */
  private byte[] writeTLV(int tag, byte[] value) {
    DatagramWriter writer = new DatagramWriter();

    // write the tag
    writer.write(tag, OCTET_BITS);

    switch (tag) {
    case BIT_STRING_TAG:
      // add the unused field to the value, as described here:
      // http://msdn.microsoft.com/en-us/library/windows/desktop/bb540792(v=vs.85).aspx
      byte[] unusedByte = new byte[1];

      // in our cases, there are never unused bits in the last byte
      unusedByte[0] = 0x00;
      value = ByteArrayUtils.concatenate(unusedByte, value);
      break;

    default:
      break;
    }

    int length = value.length;
    if (length > 127) {
      /*
       * If it is more than 127 bytes, bit 7 of the Length field is set to
       * 1 and bits 6 through 0 specify the number of additional bytes
       * used to identify the content length.
       */
      int additionalBytes = 0;
      if (length >= 16777216) { // 2^24
        additionalBytes = 4;
      } else if (length >= 65536) { // 2^16
        additionalBytes = 3;
      } else if (length >= 256) { // 2^8
        additionalBytes = 2;
      } else {
        additionalBytes = 1;
      }
      int lengthField = 0x80;
      lengthField += additionalBytes;
      writer.write(lengthField, OCTET_BITS);
      writer.write(length, additionalBytes * OCTET_BITS);

    } else {
      /*
       * If the SEQUENCE contains fewer than 128 bytes, the Length field
       * of the TLV triplet requires only one byte to specify the content
       * length.
       */
      writer.write(length, OCTET_BITS);
    }
    writer.writeBytes(value);

    return writer.toByteArray();
  }

  /**
   * Decodes a given byte stream into a OBJECT IDENTIFIER data type. See <a
   * href=
   * "http://msdn.microsoft.com/en-us/library/windows/desktop/bb540809(v=vs.85).aspx"
   * >OBJECT IDENTIFIER</a> for details.
   *
   * @param encoded
   *            the encoded object identifier.
   * @return the object identifier.
   */
  public static int[] decodeOID(byte[] encoded) {
    List<Integer> list = new ArrayList<Integer>();

    int value = encoded[0];
    int first = value / 40;
    int second = value % 40;

    list.add(first);
    list.add(second);

    value = 0;
    List<Byte> values = new ArrayList<Byte>();
    for (int i = 1; i < encoded.length; i++) {
      byte b = encoded[i];
      values.add(b);

      // check if the highest bit of the byte is set to 1, then it's a
      // multiple byte encoding
      if ((b & 0x80) > 0) {
        // do nothing, encoding of this values not finished
      } else {
        // value encoded in on byte or last byte of multiple encoded
        // value
        Collections.reverse(values);
        int length = values.size();
        int v = 0;
        for (int j = length - 1; j >= 0; j--) {

          if (j > 0) {
            // remove the leftmost bit and "multiply" according to
            // order
            v += ((int) (values.get(j) & 0x7F)) << (7 * j);
          } else {
            // set the most left bit of the byte to zero indicating
            // this is the last byte
            // 0x7F = 0111 1111
            v += values.get(j);
          }

        }
        list.add(v);

        values.clear();
      }
    }
    int[] result = new int[list.size()];
    for (int i = 0; i < list.size(); i++) {
      result[i] = list.get(i);
    }

    return result;
  }

  /**
   * Encodes an OBJECT IDENTIFIER data type into its byte representations. See
   * <a href=
   * "http://msdn.microsoft.com/en-us/library/windows/desktop/bb540809(v=vs.85).aspx"
   * >OBJECT IDENTIFIER</a> for details.
   *
   * @param oid
   *            the object identifier.
   * @return the encoded ibject identifier.
   */
  public static byte[] encodeOID(int[] oid) {

    byte[] encoded = new byte[0];

    // first two nodes combined
    byte[] firstTwo = new byte[1];
    firstTwo[0] = (byte) (40 * oid[0] + oid[1]);
    encoded = ByteArrayUtils.concatenate(encoded, firstTwo);

    for (int i = 2; i < oid.length; i++) {
      int value = oid[i];

      int length;
      if (value >= 268435456) { // 2^28
        length = 5;
      } else if (value >= 2097152) { // 2^21
        length = 4;
      } else if (value >= 16384) { // 2^14
        length = 3;
      } else if (value >= 128) { // 2^7
        length = 2;
      } else {
        length = 1;
      }

      for (int j = length - 1; j >= 0; j--) {
        byte[] b = new byte[1];
        if (j > 0) {
          // Set the most left bit to 1 indicating that there follows
          // another byte
          // 0x80 = 1000 0000
          b[0] = (byte) (((value >> (7 * j)) & 0x7F) | 0x80);
        } else {
          // set the most left bit of the byte to zero indicating this
          // is the last byte
          // 0x7F = 0111 1111
          b[0] = (byte) (value & 0x7F);
        }
        encoded = ByteArrayUtils.concatenate(encoded, b);
      }
    }

    return encoded;
  }

  // Getters and Setters ////////////////////////////////////////////

  public void setObjectIdentifier(int[] oid) {
    if (algorithmOID != null) {
      parametersOID = oid;
    } else {
      algorithmOID = oid;
    }
  }

  public void setBitString(byte[] bitString) {
    subjectPublicKey = bitString;
  }

  public byte[] getSubjectPublicKey() {
    return subjectPublicKey;
  }

  public int[] getAlgorithmOID() {
    return algorithmOID;
  }

  public int[] getParametersOID() {
    return parametersOID;
  }

}
TOP

Related Classes of ch.ethz.inf.vs.scandium.dtls.RawPublicKey

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.