From 4a51a8f96715ffb2a42189b93b9fa91b453b8530 Mon Sep 17 00:00:00 2001 From: sg481n Date: Thu, 3 Aug 2017 17:39:12 -0400 Subject:  [AAF-21] Initial code import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ia1dd196befd061f6ba0c2be6bf4456a30ea50f97 Signed-off-by: sg481n --- core/src/main/java/com/att/cadi/Symm.java | 812 ++++++++++++++++++++++++++++++ 1 file changed, 812 insertions(+) create mode 100644 core/src/main/java/com/att/cadi/Symm.java (limited to 'core/src/main/java/com/att/cadi/Symm.java') diff --git a/core/src/main/java/com/att/cadi/Symm.java b/core/src/main/java/com/att/cadi/Symm.java new file mode 100644 index 0000000..0b34b58 --- /dev/null +++ b/core/src/main/java/com/att/cadi/Symm.java @@ -0,0 +1,812 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * ============LICENSE_END==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Random; + +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; + +import com.att.cadi.Access.Level; +import com.att.cadi.config.Config; + +/** + * Key Conversion, primarily "Base64" + * + * Base64 is required for "Basic Authorization", which is an important part of the overall CADI Package. + * + * Note: This author found that there is not a "standard" library for Base64 conversion within Java. + * The source code implementations available elsewhere were surprisingly inefficient, requiring, for + * instance, multiple string creation, on a transaction pass. Integrating other packages that might be + * efficient enough would put undue Jar File Dependencies given this Framework should have none-but-Java + * dependencies. + * + * The essential algorithm is good for a symmetrical key system, as Base64 is really just + * a symmetrical key that everyone knows the values. + * + * This code is quite fast, taking about .016 ms for encrypting, decrypting and even .08 for key + * generation. The speed quality, especially of key generation makes this a candidate for a short term token + * used for identity. + * + * It may be used to easily avoid placing Clear-Text passwords in configurations, etc. and contains + * supporting functions such as 2048 keyfile generation (see keygen). This keyfile should, of course, + * be set to "400" (Unix) and protected as any other mechanism requires. + * + * However, this algorithm has not been tested against hackers. Until such a time, utilize more tested + * packages to protect Data, especially sensitive data at rest (long term). + * + */ +public class Symm { + private static final byte[] DOUBLE_EQ = new byte[] {'=','='}; + public static final String ENC = "enc:"; + private static final SecureRandom random = new SecureRandom(); + + public final char[] codeset; + private final int splitLinesAt; + private final String encoding; + private final Convert convert; + private final boolean endEquals; + //Note: AES Encryption is not Thread Safe. It is Synchronized + private static AES aes = null; // only initialized from File, and only if needed for Passwords + + /** + * This is the standard base64 Key Set. + * RFC 2045 + */ + public static final Symm base64 = new Symm( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray() + ,76, Config.UTF_8,true); + + public static final Symm base64noSplit = new Symm( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray() + ,Integer.MAX_VALUE, Config.UTF_8,true); + + /** + * This is the standard base64 set suitable for URLs and Filenames + * RFC 4648 + */ + public static final Symm base64url = new Symm( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".toCharArray() + ,76, Config.UTF_8,true); + + /** + * A Password set, using US-ASCII + * RFC 4648 + */ + public static final Symm encrypt = new Symm(base64url.codeset,1024, "US-ASCII", false); + + /** + * A typical set of Password Chars + * Note, this is too large to fit into the algorithm. Only use with PassGen + */ + private static char passChars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+!@#$%^&*(){}[]?:;,.".toCharArray(); + + + + /** + * Use this to create special case Case Sets and/or Line breaks + * + * If you don't know why you need this, use the Singleton Method + * + * @param codeset + * @param split + */ + public Symm(char[] codeset, int split, String charset, boolean useEndEquals) { + this.codeset = codeset; + splitLinesAt = split; + encoding = charset; + endEquals = useEndEquals; + char prev = 0, curr=0, first = 0; + int offset=Integer.SIZE; // something that's out of range for integer array + + // There can be time efficiencies gained when the underlying keyset consists mainly of ordered + // data (i.e. abcde...). Therefore, we'll quickly analyze the keyset. If it proves to have + // too much entropy, the "Unordered" algorithm, which is faster in such cases is used. + ArrayList la = new ArrayList(); + for(int i=0;icodeset.length/3) { + convert = new Unordered(codeset); + } else { // too random to get speed enhancement from range algorithm + int[][] range = new int[la.size()][]; + la.toArray(range); + convert = new Ordered(range); + } + } + + public Symm copy(int lines) { + return new Symm(codeset,lines,encoding,endEquals); + } + + // Only used by keygen, which is intentionally randomized. Therefore, always use unordered + private Symm(char[] codeset, Symm parent) { + this.codeset = codeset; + splitLinesAt = parent.splitLinesAt; + endEquals = parent.endEquals; + encoding = parent.encoding; + convert = new Unordered(codeset); + } + + /** + * Obtain the base64() behavior of this class, for use in standard BASIC AUTH mechanism, etc. + * @return + */ + @Deprecated + public static final Symm base64() { + return base64; + } + + /** + * Obtain the base64() behavior of this class, for use in standard BASIC AUTH mechanism, etc. + * No Line Splitting + * @return + */ + @Deprecated + public static final Symm base64noSplit() { + return base64noSplit; + } + + /** + * Obtain the base64 "URL" behavior of this class, for use in File Names, etc. (no "/") + */ + @Deprecated + public static final Symm base64url() { + return base64url; + } + + /** + * Obtain a special ASCII version for Scripting, with base set of base64url use in File Names, etc. (no "/") + */ + public static final Symm baseCrypt() { + return encrypt; + } + + /* + * Note: AES Encryption is NOT thread-safe. Must surround entire use with synchronized + */ + private synchronized void exec(AESExec exec) throws IOException { + if(aes == null) { + try { + byte[] bytes = new byte[AES.AES_KEY_SIZE/8]; + int offset = (Math.abs(codeset[0])+47)%(codeset.length-bytes.length); + for(int i=0;i=0) { + if(line>=splitLinesAt) { + os.write('\n'); + line = 0; + } + switch(++idx) { // 1 based reading, slightly faster ++ + case 1: // ptr is the first 6 bits of read + os.write(codeset[read>>2]); + prev = read; + break; + case 2: // ptr is the last 2 bits of prev followed by the first 4 bits of read + os.write(codeset[((prev & 0x03)<<4) | (read>>4)]); + prev = read; + break; + default: //(3+) + // Char 1 is last 4 bits of prev plus the first 2 bits of read + // Char 2 is the last 6 bits of read + os.write(codeset[(((prev & 0xF)<<2) | (read>>6))]); + if(line==splitLinesAt) { // deal with line splitting for two characters + os.write('\n'); + line=0; + } + os.write(codeset[(read & 0x3F)]); + ++line; + idx = 0; + prev = 0; + } + ++line; + } else { // deal with any remaining bits from Prev, then pad + switch(idx) { + case 1: // just the last 2 bits of prev + os.write(codeset[(prev & 0x03)<<4]); + if(endEquals)os.write(DOUBLE_EQ); + break; + case 2: // just the last 4 bits of prev + os.write(codeset[(prev & 0xF)<<2]); + if(endEquals)os.write('='); + break; + } + idx = 0; + } + + } while(go); + } + + public void decode(InputStream is, OutputStream os, int skip) throws IOException { + is.skip(skip); + decode(is,os); + } + + /** + * Decode InputStream onto OutputStream + * @param is + * @param os + * @throws IOException + */ + public void decode(InputStream is, OutputStream os) throws IOException { + int read, idx=0; + int prev=0, index; + while((read = is.read())>=0) { + index = convert.convert(read); + if(index>=0) { + switch(++idx) { // 1 based cases, slightly faster ++ + case 1: // index goes into first 6 bits of prev + prev = index<<2; + break; + case 2: // write second 2 bits of into prev, write byte, last 4 bits go into prev + os.write((byte)(prev|(index>>4))); + prev = index<<4; + break; + case 3: // first 4 bits of index goes into prev, write byte, last 2 bits go into prev + os.write((byte)(prev|(index>>2))); + prev = index<<6; + break; + default: // (3+) | prev and last six of index + os.write((byte)(prev|(index&0x3F))); + idx = prev = 0; + } + } + }; + os.flush(); + } + + /** + * Interface to allow this class to choose which algorithm to find index of character in Key + * + */ + private interface Convert { + public int convert(int read) throws IOException; + } + + /** + * Ordered uses a range of orders to compare against, rather than requiring the investigation + * of every character needed. + * + */ + private static final class Ordered implements Convert { + private int[][] range; + public Ordered(int[][] range) { + this.range = range; + } + public int convert(int read) throws IOException { + switch(read) { + case -1: + case '=': + case '\n': + return -1; + } + for(int i=0;i= range[i][0] && read<=range[i][1]) { + return read-range[i][2]; + } + } + throw new IOException("Unacceptable Character in Stream"); + } + } + + /** + * Unordered, i.e. the key is purposely randomized, simply has to investigate each character + * until we find a match. + * + */ + private static final class Unordered implements Convert { + private char[] codec; + public Unordered(char[] codec) { + this.codec = codec; + } + public int convert(int read) throws IOException { + switch(read) { + case -1: + case '=': + case '\n': + return -1; + } + for(int i=0;i=0) { + index = o.next(); + if(index<0 || index>=codeset.length) { + System.out.println("uh, oh"); + } + if(right) { // alternate going left or right to find the next open slot (keeps it from taking too long to hit something) + for(int j=index;j=0;--j) { + if(seq[j]==0) { + seq[j]=codeset[filled]; + --filled; + break; + } + } + right = true; + } + } + return new Symm(seq,this); + } +} -- cgit 1.2.3-korg