/* * Part of the CCNx Java Library. * * Copyright (C) 2008-2010 Palo Alto Research Center, Inc. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. * This library 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 * Lesser General Public License for more details. You should have received * a copy of the GNU Lesser General Public License along with this library; * if not, write to the Free Software Foundation, Inc., 51 Franklin Street, * Fifth Floor, Boston, MA 02110-1301 USA. */ package org.ccnx.ccn.impl.security.crypto; import java.io.IOException; import java.io.InputStream; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateEncodingException; import org.ccnx.ccn.impl.encoding.XMLEncodable; import org.ccnx.ccn.impl.security.crypto.util.DigestHelper; import org.ccnx.ccn.impl.support.Log; import org.ccnx.ccn.io.content.ContentEncodingException; /** * CCN-specific helper methods for working with digests, primarily to support Merkle trees. */ public class CCNDigestHelper extends DigestHelper { /** * Current default algorithm is SHA-256. We expect it to move to SHA3 when that * is standardized. We're doing our best to support variable algorithms in all but * core network components (digest components in ContentNames, publisher IDs), * whose digest algorithm is fixed for a given protocol version. */ public static String DEFAULT_DIGEST_ALGORITHM = "SHA-256"; public static int DEFAULT_DIGEST_LENGTH = 32; /** * Make a CCNDigestHelper using the default digest algorithm (DEFAULT_DIGEST_ALGORITHM). */ public CCNDigestHelper() { super(); if (!_md.getAlgorithm().equals(CCNDigestHelper.DEFAULT_DIGEST_ALGORITHM)) { Log.severe("Incorrect constructor in CCNDigestHelper -- picking up wrong digest algorithm!"); } } /** * Make a CCNDigestHelper that uses a specified algorithm. * @param digestAlgorithm algorithm to use * @throws NoSuchAlgorithmException if digestAlgorithm is unknown to any of our cryptography Providers */ public CCNDigestHelper(String digestAlgorithm) throws NoSuchAlgorithmException { super(digestAlgorithm); } /** * Same digest preparation algorithm as ContentObject. * @throws ContentEncodingException * @throws NoSuchAlgorithmException */ public static byte [] digestLeaf( String digestAlgorithm, XMLEncodable [] toBeSigneds, byte [][] additionalToBeSigneds) throws ContentEncodingException, NoSuchAlgorithmException { if (null == toBeSigneds) { Log.info("Value to be signed must not be null."); throw new ContentEncodingException("Unexpected null content in digestLeaf!"); } byte [][] encodedData = new byte [toBeSigneds.length + ((null != additionalToBeSigneds) ? additionalToBeSigneds.length : 0)][]; for (int i=0; i < toBeSigneds.length; ++i) { encodedData[i] = toBeSigneds[i].encode(); } if (null != additionalToBeSigneds) { for (int i=0,j=toBeSigneds.length; j < encodedData.length; ++i,++j) { encodedData[j] = additionalToBeSigneds[i]; } } return DigestHelper.digest(((null == digestAlgorithm) || (digestAlgorithm.length() == 0)) ? CCNDigestHelper.DEFAULT_DIGEST_ALGORITHM : digestAlgorithm, encodedData); } @Override public String getDefaultDigest() { return DEFAULT_DIGEST_ALGORITHM; } /** * Static digest helper. * @param content content to digest * @return digest of content using DEFAULT_DIGEST_ALGORITHM */ public static byte[] digest(byte [] content) { if (null == content) { throw new IllegalArgumentException("Content cannot be null!"); } return digest(content, 0, content.length); } /** * Static digest helper. * @param digestAlgorithm digest algorithm to use * @param content content to digest * @return digest of content using specified algorithm * @throws NoSuchAlgorithmException if the algorithm is unknown to any of our providers */ public static byte [] digest(String digestAlgorithm, byte [] content) throws NoSuchAlgorithmException { if (null == content) { throw new IllegalArgumentException("Content cannot be null!"); } return digest(digestAlgorithm, content, 0, content.length); } /** * Static digest helper. * @param content content to digest * @param offset offset into content at which to start digesting, in bytes * @param length number of bytes of content to digest * @return digest of content using DEFAULT_DIGEST_ALGORITHM */ public static byte [] digest(byte [] content, int offset, int length) { CCNDigestHelper dh = new CCNDigestHelper(); dh.update(content, offset, length); return dh.digest(); } /** * Static digest helper. * @param digestAlgorithm digest algorithm to use * @param content content to digest * @param offset offset into content at which to start digesting, in bytes * @param length number of bytes of content to digest * @return digest of content using specified algorithm * @throws NoSuchAlgorithmException if the algorithm is unknown to any of our providers */ public static byte [] digest(String digestAlgorithm, byte [] content, int offset, int length) throws NoSuchAlgorithmException { CCNDigestHelper dh = new CCNDigestHelper(digestAlgorithm); dh.update(content, offset, length); return dh.digest(); } /** * Static digest helper; returns the digest of the concatenation of two byte arrays. * If either is null, simply includes the non-null array in the digest. * @param content1 first content array to digest * @param content2 second content array to digest * @return digest of content using DEFAULT_DIGEST_ALGORITHM */ public static byte[] digest(byte [] content1, byte [] content2) { return digest(new byte [][]{content1, content2}); } /** * Static digest helper; returns the digest of the concatenation of two byte arrays. * If either is null, simply includes the non-null array in the digest. * @param digestAlgorithm digest algorithm to use * @param content1 first content array to digest * @param content2 second content array to digest * @return digest of concatenated content using specified algorithm * @throws NoSuchAlgorithmException if the algorithm is unknown to any of our providers */ public static byte [] digest(String digestAlgorithm, byte [] content1, byte [] content2) throws NoSuchAlgorithmException { return digest(digestAlgorithm, new byte [][]{content1, content2}); } /** * Static digest helper; returns the digest of the concatenation of any number of component * byte arrays. Null arrays are skipped * @param contents the arrays of content to digest * @return digest of concatenated content using DEFAULT_DIGEST_ALGORITHM */ public static byte [] digest(byte contents[][]) { CCNDigestHelper dh = new CCNDigestHelper(); for (int i=0; i < contents.length; ++i) { if (null != contents[i]) dh.update(contents[i], 0, contents[i].length); } return dh.digest(); } /** * Static digest helper; returns the digest of the concatenation of any number of component * byte arrays. Null arrays are skipped * @param digestAlgorithm digest algorithm to use * @param contents the arrays of content to digest * @return digest of concatenated content using specified algorithm * @throws NoSuchAlgorithmException if the algorithm is unknown to any of our providers */ public static byte [] digest(String digestAlgorithm, byte contents[][]) throws NoSuchAlgorithmException { CCNDigestHelper dh = new CCNDigestHelper(digestAlgorithm); for (int i=0; i < contents.length; ++i) { if (null != contents[i]) dh.update(contents[i], 0, contents[i].length); } return dh.digest(); } public static byte [] digest(String digestAlgorithm, InputStream input) throws NoSuchAlgorithmException, IOException { // Don't need data, so don't bother with digest input stream. CCNDigestHelper dh = new CCNDigestHelper(digestAlgorithm); byte [] buffer = new byte[1024]; int read = 0; while (read >= 0) { read = input.read(buffer); if (read > 0) { dh.update(buffer, 0, read); } } return dh.digest(); } public static byte [] digest(InputStream input) throws IOException { try { byte [] digest = digest(DEFAULT_DIGEST_ALGORITHM, input); return digest; } catch (java.security.NoSuchAlgorithmException ex) { // possible configuration problem Log.warning("Fatal Error: cannot find default algorithm " + DEFAULT_DIGEST_ALGORITHM); throw new RuntimeException("Error: can't find default algorithm " + DEFAULT_DIGEST_ALGORITHM + "! " + ex.toString()); } } /** * Digests some data and wraps it in an encoded PKCS#1 DigestInfo, which contains a specification * of the digestAlgorithm (as an Object Identifier, or OID wrapped in an AlgorithmIdentifier, * which for a digest algorithm typically has null parameters), and the digest itself, all encoded in DER. * @param digestAlgorithm the algorithm to use to digest (as a Java String algorithm name) * @param content the content to digest * @return a DER-encoded DigestInfo containing the digested content and the OID for digestAlgorithm * @throws CertificateEncodingException if there is an error in encoding * @throws NoSuchAlgorithmException if none of our providers recognize digestAlgorithm, or know its OID */ public static byte [] encodedDigest(String digestAlgorithm, byte [] content) throws CertificateEncodingException, NoSuchAlgorithmException { byte [] digest = digest(digestAlgorithm, content); return digestEncoder(digestAlgorithm, digest); } /** * Digests some data and wraps it in an encoded PKCS#1 DigestInfo, which contains a specification * of the digestAlgorithm (as an Object Identifier, or OID wrapped in an AlgorithmIdentifier, * which for a digest algorithm typically has null parameters), and the digest itself, all encoded in DER. * This digests content with the DEFAULT_DIGEST_ALGORITHM. * @param content the content to digest * @return a DER-encoded DigestInfo containing the content digested with DEFAULT_DIGEST_ALGORITHM * and the OID for DEFAULT_DIGEST_ALGORITHM * @throws CertificateEncodingException if there is an error in encoding */ public static byte [] encodedDigest(byte [] content) throws CertificateEncodingException { byte [] digest = digest(content); return digestEncoder(CCNDigestHelper.DEFAULT_DIGEST_ALGORITHM, digest); } }