+++ /dev/null
-/*******************************************************************************\r
- * ============LICENSE_START====================================================\r
- * * org.onap.aaf\r
- * * ===========================================================================\r
- * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.\r
- * * ===========================================================================\r
- * * Licensed under the Apache License, Version 2.0 (the "License");\r
- * * you may not use this file except in compliance with the License.\r
- * * You may obtain a copy of the License at\r
- * * \r
- * * http://www.apache.org/licenses/LICENSE-2.0\r
- * * \r
- * * Unless required by applicable law or agreed to in writing, software\r
- * * distributed under the License is distributed on an "AS IS" BASIS,\r
- * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * * See the License for the specific language governing permissions and\r
- * * limitations under the License.\r
- * * ============LICENSE_END====================================================\r
- * *\r
- * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
- * *\r
- ******************************************************************************/\r
-package org.onap.aaf.cadi;\r
-\r
-import java.io.ByteArrayInputStream;\r
-import java.io.ByteArrayOutputStream;\r
-import java.io.DataInputStream;\r
-import java.io.DataOutputStream;\r
-import java.io.File;\r
-import java.io.FileInputStream;\r
-import java.io.IOException;\r
-import java.io.InputStream;\r
-import java.io.OutputStream;\r
-import java.security.SecureRandom;\r
-import java.util.ArrayList;\r
-import java.util.Random;\r
-\r
-import javax.crypto.CipherInputStream;\r
-import javax.crypto.CipherOutputStream;\r
-\r
-import org.onap.aaf.cadi.Access.Level;\r
-import org.onap.aaf.cadi.config.Config;\r
-\r
-/**\r
- * Key Conversion, primarily "Base64"\r
- * \r
- * Base64 is required for "Basic Authorization", which is an important part of the overall CADI Package.\r
- * \r
- * Note: This author found that there is not a "standard" library for Base64 conversion within Java. \r
- * The source code implementations available elsewhere were surprisingly inefficient, requiring, for \r
- * instance, multiple string creation, on a transaction pass. Integrating other packages that might be\r
- * efficient enough would put undue Jar File Dependencies given this Framework should have none-but-Java \r
- * dependencies.\r
- * \r
- * The essential algorithm is good for a symmetrical key system, as Base64 is really just\r
- * a symmetrical key that everyone knows the values. \r
- * \r
- * This code is quite fast, taking about .016 ms for encrypting, decrypting and even .08 for key \r
- * generation. The speed quality, especially of key generation makes this a candidate for a short term token \r
- * used for identity.\r
- * \r
- * It may be used to easily avoid placing Clear-Text passwords in configurations, etc. and contains \r
- * supporting functions such as 2048 keyfile generation (see keygen). This keyfile should, of course, \r
- * be set to "400" (Unix) and protected as any other mechanism requires. \r
- * \r
- * However, this algorithm has not been tested against hackers. Until such a time, utilize more tested\r
- * packages to protect Data, especially sensitive data at rest (long term). \r
- *\r
- */\r
-public class Symm {\r
- private static final byte[] DOUBLE_EQ = new byte[] {'=','='}; \r
- public static final String ENC = "enc:";\r
- private static final SecureRandom random = new SecureRandom();\r
- \r
- public final char[] codeset;\r
- private final int splitLinesAt;\r
- private final String encoding;\r
- private final Convert convert;\r
- private final boolean endEquals;\r
- //Note: AES Encryption is not Thread Safe. It is Synchronized\r
- private static AES aes = null; // only initialized from File, and only if needed for Passwords\r
- \r
- /**\r
- * This is the standard base64 Key Set.\r
- * RFC 2045\r
- */\r
- public static final Symm base64 = new Symm(\r
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray()\r
- ,76, Config.UTF_8,true);\r
-\r
- public static final Symm base64noSplit = new Symm(\r
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray()\r
- ,Integer.MAX_VALUE, Config.UTF_8,true);\r
-\r
- /**\r
- * This is the standard base64 set suitable for URLs and Filenames\r
- * RFC 4648\r
- */\r
- public static final Symm base64url = new Symm(\r
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".toCharArray()\r
- ,76, Config.UTF_8,true);\r
-\r
- /**\r
- * A Password set, using US-ASCII\r
- * RFC 4648\r
- */\r
- public static final Symm encrypt = new Symm(base64url.codeset,1024, "US-ASCII", false);\r
-\r
- /**\r
- * A typical set of Password Chars\r
- * Note, this is too large to fit into the algorithm. Only use with PassGen\r
- */\r
- private static char passChars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+!@#$%^&*(){}[]?:;,.".toCharArray();\r
- \r
-\r
-\r
- /**\r
- * Use this to create special case Case Sets and/or Line breaks\r
- * \r
- * If you don't know why you need this, use the Singleton Method\r
- * \r
- * @param codeset\r
- * @param split\r
- */\r
- public Symm(char[] codeset, int split, String charset, boolean useEndEquals) {\r
- this.codeset = codeset;\r
- splitLinesAt = split;\r
- encoding = charset;\r
- endEquals = useEndEquals;\r
- char prev = 0, curr=0, first = 0;\r
- int offset=Integer.SIZE; // something that's out of range for integer array\r
- \r
- // There can be time efficiencies gained when the underlying keyset consists mainly of ordered \r
- // data (i.e. abcde...). Therefore, we'll quickly analyze the keyset. If it proves to have\r
- // too much entropy, the "Unordered" algorithm, which is faster in such cases is used.\r
- ArrayList<int[]> la = new ArrayList<int[]>();\r
- for(int i=0;i<codeset.length;++i) {\r
- curr = codeset[i];\r
- if(prev+1==curr) { // is next character in set\r
- prev = curr;\r
- } else {\r
- if(offset!=Integer.SIZE) { // add previous range \r
- la.add(new int[]{first,prev,offset});\r
- }\r
- first = prev = curr;\r
- offset = curr-i;\r
- }\r
- }\r
- la.add(new int[]{first,curr,offset});\r
- if(la.size()>codeset.length/3) {\r
- convert = new Unordered(codeset);\r
- } else { // too random to get speed enhancement from range algorithm\r
- int[][] range = new int[la.size()][];\r
- la.toArray(range);\r
- convert = new Ordered(range);\r
- }\r
- }\r
- \r
- public Symm copy(int lines) {\r
- return new Symm(codeset,lines,encoding,endEquals);\r
- }\r
- \r
- // Only used by keygen, which is intentionally randomized. Therefore, always use unordered\r
- private Symm(char[] codeset, Symm parent) {\r
- this.codeset = codeset;\r
- splitLinesAt = parent.splitLinesAt;\r
- endEquals = parent.endEquals;\r
- encoding = parent.encoding;\r
- convert = new Unordered(codeset);\r
- }\r
-\r
- /**\r
- * Obtain the base64() behavior of this class, for use in standard BASIC AUTH mechanism, etc.\r
- * @return\r
- */\r
- @Deprecated\r
- public static final Symm base64() {\r
- return base64;\r
- }\r
-\r
- /**\r
- * Obtain the base64() behavior of this class, for use in standard BASIC AUTH mechanism, etc. \r
- * No Line Splitting\r
- * @return\r
- */\r
- @Deprecated\r
- public static final Symm base64noSplit() {\r
- return base64noSplit;\r
- }\r
-\r
- /**\r
- * Obtain the base64 "URL" behavior of this class, for use in File Names, etc. (no "/")\r
- */\r
- @Deprecated\r
- public static final Symm base64url() {\r
- return base64url;\r
- }\r
-\r
- /**\r
- * Obtain a special ASCII version for Scripting, with base set of base64url use in File Names, etc. (no "/")\r
- */\r
- public static final Symm baseCrypt() {\r
- return encrypt;\r
- }\r
-\r
- /*\r
- * Note: AES Encryption is NOT thread-safe. Must surround entire use with synchronized\r
- */\r
- private synchronized void exec(AESExec exec) throws IOException {\r
- if(aes == null) {\r
- try {\r
- byte[] bytes = new byte[AES.AES_KEY_SIZE/8];\r
- int offset = (Math.abs(codeset[0])+47)%(codeset.length-bytes.length);\r
- for(int i=0;i<bytes.length;++i) {\r
- bytes[i] = (byte)codeset[i+offset];\r
- }\r
- aes = new AES(bytes,0,bytes.length);\r
- } catch (Exception e) {\r
- throw new IOException(e);\r
- }\r
- }\r
- exec.exec(aes);\r
- }\r
- \r
- private static interface AESExec {\r
- public void exec(AES aes) throws IOException;\r
- }\r
- \r
- public byte[] encode(byte[] toEncrypt) throws IOException {\r
- ByteArrayOutputStream baos = new ByteArrayOutputStream((int)(toEncrypt.length*1.25));\r
- encode(new ByteArrayInputStream(toEncrypt),baos);\r
- return baos.toByteArray();\r
- }\r
-\r
- public byte[] decode(byte[] encrypted) throws IOException {\r
- ByteArrayOutputStream baos = new ByteArrayOutputStream((int)(encrypted.length*1.25));\r
- decode(new ByteArrayInputStream(encrypted),baos);\r
- return baos.toByteArray();\r
- }\r
-\r
- /**\r
- * Helper function for String API of "Encode"\r
- * use "getBytes" with appropriate char encoding, etc.\r
- * \r
- * @param str\r
- * @return\r
- * @throws IOException\r
- */\r
- public String encode(String str) throws IOException {\r
- byte[] array;\r
- try { \r
- array = str.getBytes(encoding);\r
- } catch (IOException e) {\r
- array = str.getBytes(); // take default\r
- }\r
- // Calculate expected size to avoid any buffer expansion copies within the ByteArrayOutput code\r
- ByteArrayOutputStream baos = new ByteArrayOutputStream((int)(array.length*1.363)); // account for 4 bytes for 3 and a byte or two more\r
- \r
- encode(new ByteArrayInputStream(array),baos);\r
- return baos.toString(encoding);\r
- }\r
- \r
- /**\r
- * Helper function for the String API of "Decode"\r
- * use "getBytes" with appropriate char encoding, etc.\r
- * @param str\r
- * @return\r
- * @throws IOException\r
- */\r
- public String decode(String str) throws IOException {\r
- byte[] array;\r
- try { \r
- array = str.getBytes(encoding);\r
- } catch (IOException e) {\r
- array = str.getBytes(); // take default\r
- }\r
- // Calculate expected size to avoid any buffer expansion copies within the ByteArrayOutput code\r
- ByteArrayOutputStream baos = new ByteArrayOutputStream((int)(array.length*.76)); // Decoding is 3 bytes for 4. Allocate slightly more than 3/4s\r
- decode(new ByteArrayInputStream(array), baos);\r
- return baos.toString(encoding);\r
- }\r
-\r
- /**\r
- * Convenience Function\r
- * \r
- * encode String into InputStream and call encode(InputStream, OutputStream)\r
- * \r
- * @param string\r
- * @param out\r
- * @throws IOException\r
- */\r
- public void encode(String string, OutputStream out) throws IOException {\r
- encode(new ByteArrayInputStream(string.getBytes()),out);\r
- }\r
-\r
- /**\r
- * Convenience Function\r
- * \r
- * encode String into InputStream and call decode(InputStream, OutputStream)\r
- * \r
- * @param string\r
- * @param out\r
- * @throws IOException\r
- */\r
- public void decode(String string, OutputStream out) throws IOException {\r
- decode(new ByteArrayInputStream(string.getBytes()),out);\r
- }\r
-\r
- public void encode(InputStream is, OutputStream os, byte[] prefix) throws IOException {\r
- os.write(prefix);\r
- encode(is,os);\r
- }\r
-\r
- /** \r
- * encode InputStream onto Output Stream\r
- * \r
- * @param is\r
- * @param estimate\r
- * @return\r
- * @throws IOException\r
- */\r
- public void encode(InputStream is, OutputStream os) throws IOException {\r
- // StringBuilder sb = new StringBuilder((int)(estimate*1.255)); // try to get the right size of StringBuilder from start.. slightly more than 1.25 times \r
- int prev=0;\r
- int read, idx=0, line=0;\r
- boolean go;\r
- do {\r
- read = is.read();\r
- if(go = read>=0) {\r
- if(line>=splitLinesAt) {\r
- os.write('\n');\r
- line = 0;\r
- }\r
- switch(++idx) { // 1 based reading, slightly faster ++\r
- case 1: // ptr is the first 6 bits of read\r
- os.write(codeset[read>>2]);\r
- prev = read;\r
- break;\r
- case 2: // ptr is the last 2 bits of prev followed by the first 4 bits of read\r
- os.write(codeset[((prev & 0x03)<<4) | (read>>4)]);\r
- prev = read;\r
- break;\r
- default: //(3+) \r
- // Char 1 is last 4 bits of prev plus the first 2 bits of read\r
- // Char 2 is the last 6 bits of read\r
- os.write(codeset[(((prev & 0xF)<<2) | (read>>6))]);\r
- if(line==splitLinesAt) { // deal with line splitting for two characters\r
- os.write('\n');\r
- line=0;\r
- }\r
- os.write(codeset[(read & 0x3F)]);\r
- ++line;\r
- idx = 0;\r
- prev = 0;\r
- }\r
- ++line;\r
- } else { // deal with any remaining bits from Prev, then pad\r
- switch(idx) {\r
- case 1: // just the last 2 bits of prev\r
- os.write(codeset[(prev & 0x03)<<4]);\r
- if(endEquals)os.write(DOUBLE_EQ);\r
- break;\r
- case 2: // just the last 4 bits of prev\r
- os.write(codeset[(prev & 0xF)<<2]);\r
- if(endEquals)os.write('=');\r
- break;\r
- }\r
- idx = 0;\r
- }\r
- \r
- } while(go);\r
- }\r
-\r
- public void decode(InputStream is, OutputStream os, int skip) throws IOException {\r
- is.skip(skip);\r
- decode(is,os);\r
- }\r
-\r
- /**\r
- * Decode InputStream onto OutputStream\r
- * @param is\r
- * @param os\r
- * @throws IOException\r
- */\r
- public void decode(InputStream is, OutputStream os) throws IOException {\r
- int read, idx=0;\r
- int prev=0, index;\r
- while((read = is.read())>=0) {\r
- index = convert.convert(read);\r
- if(index>=0) {\r
- switch(++idx) { // 1 based cases, slightly faster ++\r
- case 1: // index goes into first 6 bits of prev\r
- prev = index<<2; \r
- break;\r
- case 2: // write second 2 bits of into prev, write byte, last 4 bits go into prev\r
- os.write((byte)(prev|(index>>4)));\r
- prev = index<<4;\r
- break;\r
- case 3: // first 4 bits of index goes into prev, write byte, last 2 bits go into prev\r
- os.write((byte)(prev|(index>>2)));\r
- prev = index<<6;\r
- break;\r
- default: // (3+) | prev and last six of index\r
- os.write((byte)(prev|(index&0x3F)));\r
- idx = prev = 0;\r
- }\r
- }\r
- };\r
- os.flush();\r
- }\r
- \r
- /**\r
- * Interface to allow this class to choose which algorithm to find index of character in Key\r
- *\r
- */\r
- private interface Convert {\r
- public int convert(int read) throws IOException;\r
- }\r
-\r
- /**\r
- * Ordered uses a range of orders to compare against, rather than requiring the investigation\r
- * of every character needed.\r
- *\r
- */\r
- private static final class Ordered implements Convert {\r
- private int[][] range;\r
- public Ordered(int[][] range) {\r
- this.range = range;\r
- }\r
- public int convert(int read) throws IOException {\r
- switch(read) {\r
- case -1: \r
- case '=':\r
- case '\n': \r
- return -1;\r
- }\r
- for(int i=0;i<range.length;++i) {\r
- if(read >= range[i][0] && read<=range[i][1]) {\r
- return read-range[i][2];\r
- }\r
- }\r
- throw new IOException("Unacceptable Character in Stream");\r
- }\r
- }\r
- \r
- /**\r
- * Unordered, i.e. the key is purposely randomized, simply has to investigate each character\r
- * until we find a match.\r
- *\r
- */\r
- private static final class Unordered implements Convert {\r
- private char[] codec;\r
- public Unordered(char[] codec) {\r
- this.codec = codec;\r
- }\r
- public int convert(int read) throws IOException {\r
- switch(read) {\r
- case -1: \r
- case '=':\r
- case '\n': \r
- return -1;\r
- }\r
- for(int i=0;i<codec.length;++i) {\r
- if(codec[i]==read)return i;\r
- }\r
- // don't give clue in Encryption mode\r
- throw new IOException("Unacceptable Character in Stream");\r
- }\r
- }\r
-\r
- /**\r
- * Generate a 2048 based Key from which we extract our code base\r
- * \r
- * @return\r
- * @throws IOException\r
- */\r
- public byte[] keygen() throws IOException {\r
- byte inkey[] = new byte[0x600];\r
- new SecureRandom().nextBytes(inkey);\r
- ByteArrayOutputStream baos = new ByteArrayOutputStream(0x800);\r
- base64url.encode(new ByteArrayInputStream(inkey), baos);\r
- return baos.toByteArray();\r
- }\r
- \r
- // A class allowing us to be less predictable about significant digits (i.e. not picking them up from the\r
- // beginning, and not picking them up in an ordered row. Gives a nice 2048 with no visible patterns.\r
- private class Obtain {\r
- private int last;\r
- private int skip;\r
- private int length;\r
- private byte[] key;\r
- \r
- private Obtain(Symm b64, byte[] key) {\r
- skip = Math.abs(key[key.length-13]%key.length);\r
- if((key.length&0x1) == (skip&0x1)) { // if both are odd or both are even\r
- ++skip;\r
- }\r
- length = b64.codeset.length;\r
- last = 17+length%59; // never start at beginning\r
- this.key = key;\r
- }\r
- \r
- private int next() {\r
- return Math.abs(key[(++last*skip)%key.length])%length;\r
- }\r
- };\r
- \r
- /**\r
- * Obtain a Symm from "keyfile" (Config.KEYFILE) property\r
- * \r
- * @param acesss\r
- * @return\r
- */\r
- public static Symm obtain(Access access) {\r
- Symm symm = Symm.baseCrypt();\r
-\r
- String keyfile = access.getProperty(Config.CADI_KEYFILE,null);\r
- if(keyfile!=null) {\r
- File file = new File(keyfile);\r
- try {\r
- access.log(Level.INIT, Config.CADI_KEYFILE,"points to",file.getCanonicalPath());\r
- } catch (IOException e1) {\r
- access.log(Level.INIT, Config.CADI_KEYFILE,"points to",file.getAbsolutePath());\r
- }\r
- if(file.exists()) {\r
- try {\r
- FileInputStream fis = new FileInputStream(file);\r
- try {\r
- symm = Symm.obtain(fis);\r
- } finally {\r
- try {\r
- fis.close();\r
- } catch (IOException e) {\r
- }\r
- }\r
- } catch (IOException e) {\r
- access.log(e, "Cannot load keyfile");\r
- }\r
- }\r
- }\r
- return symm;\r
- }\r
- /**\r
- * Create a new random key \r
- */\r
- public Symm obtain() throws IOException {\r
- byte inkey[] = new byte[0x800];\r
- new SecureRandom().nextBytes(inkey);\r
- return obtain(inkey);\r
- }\r
- \r
- /**\r
- * Obtain a Symm from 2048 key from a String\r
- * \r
- * @param key\r
- * @return\r
- * @throws IOException\r
- */\r
- public static Symm obtain(String key) throws IOException {\r
- return obtain(new ByteArrayInputStream(key.getBytes()));\r
- }\r
- \r
- /**\r
- * Obtain a Symm from 2048 key from a Stream\r
- * \r
- * @param is\r
- * @return\r
- * @throws IOException\r
- */\r
- public static Symm obtain(InputStream is) throws IOException {\r
- ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
- try {\r
- base64url.decode(is, baos);\r
- } catch (IOException e) {\r
- // don't give clue\r
- throw new IOException("Invalid Key");\r
- }\r
- byte[] bkey = baos.toByteArray();\r
- if(bkey.length<0x88) { // 2048 bit key\r
- throw new IOException("Invalid key");\r
- }\r
- return baseCrypt().obtain(bkey);\r
- }\r
-\r
- /**\r
- * Convenience for picking up Keyfile\r
- * \r
- * @param f\r
- * @return\r
- * @throws IOException\r
- */\r
- public static Symm obtain(File f) throws IOException {\r
- FileInputStream fis = new FileInputStream(f);\r
- try {\r
- return obtain(fis);\r
- } finally {\r
- fis.close();\r
- }\r
- }\r
- /**\r
- * Decrypt into a String\r
- *\r
- * Convenience method\r
- * \r
- * @param password\r
- * @return\r
- * @throws IOException\r
- */\r
- public String enpass(String password) throws IOException {\r
- ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
- enpass(password,baos);\r
- return new String(baos.toByteArray());\r
- }\r
-\r
- /**\r
- * Create an encrypted password, making sure that even short passwords have a minimum length.\r
- * \r
- * @param password\r
- * @param os\r
- * @throws IOException\r
- */\r
- public void enpass(final String password, final OutputStream os) throws IOException {\r
- final ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
- DataOutputStream dos = new DataOutputStream(baos);\r
- byte[] bytes = password.getBytes();\r
- if(this.getClass().getSimpleName().startsWith("base64")) { // don't expose randomization\r
- dos.write(bytes);\r
- } else {\r
- \r
- Random r = new SecureRandom();\r
- int start = 0;\r
- byte b;\r
- for(int i=0;i<3;++i) {\r
- dos.writeByte(b=(byte)r.nextInt());\r
- start+=Math.abs(b);\r
- }\r
- start%=0x7;\r
- for(int i=0;i<start;++i) {\r
- dos.writeByte(r.nextInt());\r
- }\r
- dos.writeInt((int)System.currentTimeMillis());\r
- int minlength = Math.min(0x9,bytes.length);\r
- dos.writeByte(minlength); // expect truncation\r
- if(bytes.length<0x9) {\r
- for(int i=0;i<bytes.length;++i) {\r
- dos.writeByte(r.nextInt());\r
- dos.writeByte(bytes[i]);\r
- }\r
- // make sure it's long enough\r
- for(int i=bytes.length;i<0x9;++i) {\r
- dos.writeByte(r.nextInt());\r
- }\r
- } else {\r
- dos.write(bytes);\r
- }\r
- }\r
- \r
- // 7/21/2016 jg add AES Encryption to the mix\r
- exec(new AESExec() {\r
- @Override\r
- public void exec(AES aes) throws IOException {\r
- CipherInputStream cis = aes.inputStream(new ByteArrayInputStream(baos.toByteArray()), true);\r
- try {\r
- encode(cis,os);\r
- } finally {\r
- os.flush();\r
- cis.close();\r
- }\r
- }\r
- });\r
- synchronized(ENC) {\r
- }\r
- }\r
-\r
- /**\r
- * Decrypt a password into a String\r
- * \r
- * Convenience method\r
- * \r
- * @param password\r
- * @return\r
- * @throws IOException\r
- */\r
- public String depass(String password) throws IOException {\r
- if(password==null)return null;\r
- ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
- depass(password,baos);\r
- return new String(baos.toByteArray());\r
- }\r
- \r
- /**\r
- * Decrypt a password\r
- * \r
- * Skip Symm.ENC\r
- * \r
- * @param password\r
- * @param os\r
- * @return\r
- * @throws IOException\r
- */\r
- public long depass(final String password, final OutputStream os) throws IOException {\r
- int offset = password.startsWith(ENC)?4:0;\r
- final ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
- final ByteArrayInputStream bais = new ByteArrayInputStream(password.getBytes(),offset,password.length()-offset);\r
- exec(new AESExec() {\r
- @Override\r
- public void exec(AES aes) throws IOException {\r
- CipherOutputStream cos = aes.outputStream(baos, false);\r
- decode(bais,cos);\r
- cos.close(); // flush\r
- }\r
- });\r
- byte[] bytes = baos.toByteArray();\r
- DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes));\r
- long time;\r
- if(this.getClass().getSimpleName().startsWith("base64")) { // don't expose randomization\r
- os.write(bytes);\r
- time = 0L;\r
- } else {\r
- int start=0;\r
- for(int i=0;i<3;++i) {\r
- start+=Math.abs(dis.readByte());\r
- }\r
- start%=0x7;\r
- for(int i=0;i<start;++i) {\r
- dis.readByte();\r
- }\r
- time = (dis.readInt() & 0xFFFF)|(System.currentTimeMillis()&0xFFFF0000);\r
- int minlength = dis.readByte();\r
- if(minlength<0x9){\r
- DataOutputStream dos = new DataOutputStream(os);\r
- for(int i=0;i<minlength;++i) {\r
- dis.readByte();\r
- dos.writeByte(dis.readByte());\r
- }\r
- } else {\r
- int pre =((Byte.SIZE*3+Integer.SIZE+Byte.SIZE)/Byte.SIZE)+start; \r
- os.write(bytes, pre, bytes.length-pre);\r
- }\r
- }\r
- return time;\r
- }\r
-\r
- public static String randomGen(int numBytes) {\r
- return randomGen(passChars,numBytes); \r
- }\r
- \r
- public static String randomGen(char[] chars ,int numBytes) {\r
- int rint;\r
- StringBuilder sb = new StringBuilder(numBytes);\r
- for(int i=0;i<numBytes;++i) {\r
- rint = random.nextInt(chars.length);\r
- sb.append(chars[rint]);\r
- }\r
- return sb.toString();\r
- }\r
- // Internal mechanism for helping to randomize placement of characters within a Symm codeset\r
- // Based on an incoming data stream (originally created randomly, but can be recreated within \r
- // 2048 key), go after a particular place in the new codeset. If that codeset spot is used, then move\r
- // right or left (depending on iteration) to find the next available slot. In this way, key generation \r
- // is speeded up by only enacting N iterations, but adds a spreading effect of the random number stream, so that keyset is also\r
- // shuffled for a good spread. It is, however, repeatable, given the same number set, allowing for \r
- // quick recreation when the official stream is actually obtained.\r
- public Symm obtain(byte[] key) throws IOException {\r
- try {\r
- byte[] bytes = new byte[AES.AES_KEY_SIZE/8];\r
- int offset = (Math.abs(key[(47%key.length)])+137)%(key.length-bytes.length);\r
- for(int i=0;i<bytes.length;++i) {\r
- bytes[i] = key[i+offset];\r
- }\r
-\r
- aes = new AES(bytes,0,bytes.length);\r
- } catch (Exception e) {\r
- throw new IOException(e);\r
- }\r
- int filled = codeset.length;\r
- char[] seq = new char[filled];\r
- int end = filled--;\r
- \r
- boolean right = true;\r
- int index;\r
- Obtain o = new Obtain(this,key);\r
- \r
- while(filled>=0) {\r
- index = o.next();\r
- if(index<0 || index>=codeset.length) {\r
- System.out.println("uh, oh");\r
- }\r
- if(right) { // alternate going left or right to find the next open slot (keeps it from taking too long to hit something) \r
- for(int j=index;j<end;++j) {\r
- if(seq[j]==0) {\r
- seq[j]=codeset[filled];\r
- --filled;\r
- break;\r
- }\r
- }\r
- right = false;\r
- } else {\r
- for(int j=index;j>=0;--j) {\r
- if(seq[j]==0) {\r
- seq[j]=codeset[filled];\r
- --filled;\r
- break;\r
- }\r
- }\r
- right = true;\r
- }\r
- }\r
- return new Symm(seq,this);\r
- }\r
-}\r