/* * [Tools.java] * * Summary: miscellaneous methods needed by Transporter classes. * * Copyright: (c) 2006-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.4 2006-03-05 reformat with IntelliJ and add Javadoc. */ package com.mindprod.transporter; import java.math.BigInteger; import java.util.ArrayList; import java.util.Random; import static java.lang.System.*; /** * miscellaneous methods needed by Transporter classes. * * @author Roedy Green, Canadian Mind Products * @version 1.4 2006-03-05 reformat with IntelliJ and add Javadoc. * @since 2006 */ final class Tools { /** * Biggest possible keysize */ public static final int BIGGEST_KEYSIZE = 4096; /** * Smallest possible keysize. */ public static final int SMALLEST_KEYSIZE = 256; /** * a small prime used to tweak the algorithm. We choose 17. Don't change this without regenerating all keys. */ public static final BigInteger smallPrime = new BigInteger( "17" ); /** * usual size of unencrypted serialised message in bytes. Making it too big wastes RAM. Making it too small wastes * cpu time in processing large messages in chunks. */ static final int TYPICAL_MESSAGE_SIZE = 1024 * 4; /** * need not be SecureRandom. Used to generate gibberish for salt. */ private static final Random wheel = new Random(); /** * Show bytes. Debugging tool to display a byte array * * @param marker text to be displayed to identify array being dumped. * @param b byte array to be displayed. */ public static void dumpBytes( String marker, byte[] b ) { out.print( marker + " " ); if ( b == null ) { out.println( "null" ); } else if ( b.length == 0 ) { out.println( "empty" ); } else { out.print( "len: " + b.length + " bytes: " ); for ( final byte aB : b ) { out.print( ( int ) aB + " " ); } out.println(); } } /** * like Arrays.equals, compare two byte arrays. * * @param a first array * @param b second array * * @return true if arrays have equal elements. */ public static boolean equals( byte[] a, byte[] b ) { if ( a == b ) { return true; } if ( a == null || b == null ) { return false; } if ( a.length != b.length ) { return false; } int length = a.length; for ( int i = 0; i < length; i++ ) { if ( a[ i ] != b[ i ] ) { return false; } } return true; } /** * extract 16 bit length from start of a byte block. Stored MSB first. * * @param block block containing the length bits. * @param offset where to find the length bits. * * @return recovered length of block. */ public static int getBlockLength( byte[] block, int offset ) { // MSB first int hi = block[ offset ] & 0xff; int lo = block[ offset + 1 ] & 0xff; return ( hi << 8 ) + lo; } /** * insert subarray of source into target. Like arraycopy but with parms in different order, and only does byte[]. * * @param target Where bytes go. * @param targetOffset offset in target where bytes go. * @param source source array for bytes. * @param sourceOffset offset in source to find the bytes. * @param length how many bytes to copy. */ public static void insert( byte[] target, int targetOffset, byte[] source, int sourceOffset, int length ) { System.arraycopy( source, sourceOffset + 0, target, targetOffset + 0, length ); } /** * Join all the byte[] blocks in the Vector into one long byte[]. Use Vector instead of ArrayList so will work on * Java 1.1. * * @param blocks Vector of blocks block * * @return all blocks joined end to end. */ public static byte[] join( ArrayList blocks ) { int size = blocks.size(); if ( size == 1 ) { return blocks.get( 0 ); } // find out how big the result will be. int overallLength = 0; for ( byte[] block1 : blocks ) { overallLength += block1.length; } byte[] result = new byte[ overallLength ]; int offset = 0; for ( byte[] block : blocks ) { int length = block.length; insert( result, offset, block, 0, length ); offset += length; } return result; } /** * prepend 16-bit unsigned length on a block * * @param block block * * @return block with two byte header containing length. */ public static byte[] prependLength( byte[] block ) { int length = block.length; byte[] result = new byte[ length + 2 ]; result[ 0 ] = ( byte ) ( length >>> 8 );// hi result[ 1 ] = ( byte ) ( length );// lo insert( result, 2, block, 0, length ); return result; } /** * process one block of text with a private key, (used for decrypt also sign). * * @param block text to be decoded * @param k private key * * @return message RSA decrypted. Will still have salt. */ public static byte[] processWithPrivateKey( byte[] block, PrivateKey k ) { return new BigInteger( block ).modPow( k.d, k.n ).toByteArray(); } /** * process block with public key ( used for encrypt also verify ) * * @param block block of text to process * @param k public key * * @return block processed with public key, will be about 3 times longer. */ public static byte[] processWithPublicKey( byte[] block, PublicKey k ) { /* encrypted message will be 2 to 3 times longer! */ return ( new BigInteger( 1/* positive */, block ).modPow( smallPrime, k.n ) ).toByteArray(); } /** * prepend with 4-byte salt so identical messages will encrypt differently. Prepends with 4 bytes of gibberish which * are later ignored. * * @param block bytes to be salted. * * @return salted block */ public static byte[] salt( byte[] block ) { byte[] result = new byte[ block.length + 4 ]; /* * first byte ensures lead 0s are not lost when the message considered * as a number. It must be a positive random number between 1 and 127 */ result[ 0 ] = ( byte ) ( wheel.nextInt( 127 ) + 1 ); result[ 1 ] = ( byte ) ( wheel.nextInt( 256 ) ); result[ 2 ] = ( byte ) ( wheel.nextInt( 256 ) ); result[ 3 ] = ( byte ) ( wheel.nextInt( 256 ) ); Tools.insert( result, 4, block, 0, block.length ); return result; } /** * Extract subarray of a byte array. Similar to substr, but for for bytes. * * @param source array * @param offset offset in source to find wanted bytes. * @param length length of new extracted array * * @return new array. Typically you save it is over top of the source reference. */ public static byte[] subarray( byte[] source, int offset, int length ) { if ( offset == 0 && source.length == length ) { /* nothing to do */ return source; } byte[] target = new byte[ length ]; System.arraycopy( source, offset + 0, target, 0, length ); return target; } /** * remove the 4-byte salt header . * * @param saltedBlock salted block. * * @return block without salt header. */ public static byte[] unsalt( byte[] saltedBlock ) { return Tools.subarray( saltedBlock, 4, saltedBlock.length - 4 ); } }