/* IDSpace.java * * This file is part of Gzz. * * Gzz is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Gzz 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 Gzz; if not, write to the Free * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * */ /* * Written by Benja Fallenstein, Tuomas Lukka and Antti-Juhani Kaijanaho */ package gzz.mediaserver; import java.security.*; import java.io.*; /** A space of globally unique IDs. * Now with support for two ID schemes: '00' and '01'. Only '01' is * generated; '00' can be verified, but is never generated. *

* '00' IDs contain some additional information, '01' IDs contain * only an SHA-1 hash. Note that the data that produces the hash * in the '00' IDs contains the part of the ID that is not part of * the hash-- i.e., part of the ID is prepended to the actual data * before hashing. '01' hashes do not do this. At first glance, this * seems to be consistent, since there does not seem to be anything * else in the '01' IDs except the hash-- but there is something else: * the '01' ID format byte. *

* Yet, hashing that byte too would make the model harder to understand, * and it's not like there would be much point in it. So there's a minor * inconsistency between '00' and '01' IDs which you sometimes may have * to worry about. */ public class IDSpace implements Serializable { static public final IDSpace instance = new IDSpace(); transient private MessageDigest d; // transient private SecureRandom r = new SecureRandom(); transient private java.util.Random r; public IDSpace() { try { d = MessageDigest.getInstance("SHA"); } catch(Exception e) { e.printStackTrace(); System.out.println(e); throw new Error("Can't create IDSpace: "+e); } } /** Put the data in an old-style ('00') id and data arrays into the * signature object, * preparing to sign the combination. *

* The old format is: 4 bytes = integer length of the id, * the id and the data. */ public void put00Data(MessageDigest d, byte[] id, byte[] data) { int l = id.length; d.update((byte)((l >>> 24) & 0xff)); d.update((byte)((l >>> 16) & 0xff)); d.update((byte)((l >>> 8) & 0xff)); d.update((byte)(l & 0xff)); d.update(id); d.update(data); } /** Sign the specified id and data, using the private key of this * idspace. * @param id For old-style ('00') IDs, the initial part of the ID, * without the hash. For new-style IDs, 'null'. * @param data The data part of the id. */ synchronized private byte[] getHash(byte[] id, byte[] data) { d.reset(); if(id != null) put00Data(d, id, data); else d.update(data); return d.digest(); } /** Create a fresh ID for the given block of data. * The ID contains a cryptographic hash of the data to avoid * spoofing. (Actually, the current ID format is just an SHA-1 * hash with a '01' byte prepended.) * @param data The block of data that the new ID will refer to */ public Mediaserver.Id createID(byte[] data) { try { ByteArrayOutputStream os = new ByteArrayOutputStream(); //DataOutputStream dos = new DataOutputStream(os); os.write(0x01); // id format version byte[] hash = getHash(null, data); os.write(hash); return new Mediaserver.Id(os.toByteArray()); } catch(IOException e) { throw new Error("Really unexpected"); } } /** Check that the hash code in the id is correct. * Note that this function does not detect * spoofing by new ids and data blocks, but only * the changing of the data block behind a known * id. */ public boolean checkData(Mediaserver.Id idid, byte[] data) { byte[] id = idid.getBytes(); if(id[0] != 0x00 && id[0] != 0x01) throw new Error("ID format version not supported: "+id[0]); byte[] idstart = null; if(id[0] == 0x00) { idstart = new byte[id.length-20]; System.arraycopy(id, 0, idstart, 0, idstart.length); } byte[] hash = getHash(idstart, data); for(int i=0; i<20; i++) { if(hash[i] != id [ id.length - 20 + i ] ) return false; } return true; } /** Return an ID-checking InputStream. * When closed, the returned InputStream must throw an exception * if the ID is not correct. */ public InputStream checkInputStream(Mediaserver.Id idid, InputStream is) { byte[] id = idid.getBytes(); if(id[0] != 0x00 && id[0] != 0x01) throw new Error("ID format version not supported"); byte[] idstart = null; if(id[0] == 0x00) { idstart = new byte[id.length-20]; System.arraycopy(id, 0, idstart, 0, idstart.length); } byte[] hash = new byte[20]; System.arraycopy(id, id.length-20, hash, 0, 20); return new IDCheckInputStream(is, idstart, hash); } }