runHost.java |
---|
import java.io.*; import java.net.*; import java.util.*; import com.dalsemi.onewire.adapter.*; import com.dalsemi.onewire.container.*; import com.dalsemi.onewire.OneWireAccessProvider; import com.dalsemi.onewire.application.sha.*; import com.dalsemi.onewire.*; import com.dalsemi.onewire.utils.*; /** * Starts the host component for NetAdapter clients on the local machine. * If no options are specified, the default adapter for this machine is used * and the host is launched as a multi-threaded server using the defaults. * */ public class runHost { static final String strUsage = "Starts the host component for NetAdapter clients on the local machine.\n" + "If no options are specified, the default adapter for this machine is used\n" + "and the host is launched as a multi-threaded server using the defaults:\n" + "\n" + " Host Listen Port: " + AuthenticationConstants.DEFAULT_PORT + "\n" + "\n" + "syntax: java AuthHost <options>\n" + "\n" + "Options:\n" + " -props Pulls all defaults from the onewire.properties\n" + " file rather than using the defaults set in\n" + " com.dalsemi.onewire.adapter.NetAdapterConstants.\n" + " -p PATH This is the path to the SHA properties file.\n" + " -listenPort NUM Sets the host's listening port for incoming\n" + " socket connections.\n" + " -secret STRING Sets the shared secret for authenticating incoming\n" + " client connections.\n"; public static void usage() { System.out.println(); System.out.println(strUsage); System.exit(1); } public static void main(String[] args) throws Exception { String adapterName = null, adapterPort = null; int listenPort = AuthenticationConstants.DEFAULT_PORT; boolean multithread = true; String secret = AuthenticationConstants.DEFAULT_SECRET; byte[] SNum = new byte[8]; byte[] data = new byte[7]; // コプロセッサ long coprID = 0; SHAiButtonCopr copr = null; boolean useProperties = false; // ------------------------------------------------------------ // コマンドラインでsha.propertiesファイルのパスをチェックする // ------------------------------------------------------------ for(int i=0; i<args.length; i++) { char c = args[0].charAt(1); String arg = args[i].toUpperCase(); if(arg.indexOf("-P")==0) { String sha_props_path; if(arg.length()==2) sha_props_path = args[++i]; else sha_props_path = arg.substring(2); // sha.propertiesファイルを開く try { FileInputStream prop_file = new FileInputStream(sha_props_path + "sha.properties"); sha_properties = new Properties(); sha_properties.load(prop_file); } catch(Exception e) { sha_properties = null; } } else if(arg.equalsIgnoreCase("-listenPort")) { listenPort = Integer.parseInt(args[++i]); } else if(arg.equalsIgnoreCase("-secret")) { secret = args[++i]; } else if(args[0].charAt(0)!='-' || c=='h' || c=='H' || c=='?') { System.out.println("Invalid option: " + arg); usage(); } } // ------------------------------------------------------------ // コプロセッサを見つける // ------------------------------------------------------------ if(getPropertyBoolean("copr.simulated.isSimulated", false)) { String coprVMfilename = getProperty("copr.simulated.filename"); // --------------------------------------------------------- // Load emulated coprocessor // --------------------------------------------------------- try { copr = new SHAiButtonCoprVM(coprVMfilename); } catch(Exception e) { IOHelper.writeLine("Invalid Coprocessor Data File"); e.printStackTrace(); System.exit(1); } } if(copr==null) { IOHelper.writeLine("No Coprocessor found!"); System.exit(1); } IOHelper.writeLine(copr); IOHelper.writeLine(); AuthenticationHost sh = new AuthenticationHost(copr,listenPort, secret); sh.run(); } static Properties sha_properties = null; /** * 指定されたプロパティを得る。 * 以下の場所からプロパティを探す。 * 1. System.propertiesの中 * 2. カレントディレクトリにあるonewire.propertiesファイルの中あるいは * TINIの /etc/ の中 * * 引数: propName : 読まれるプロパティの文字列名 * * 戻り値: String : プロパティの値、見つからなかった場合はnull */ public static String getProperty (String propName) { // 始めにシステムプロパティを試みる try { String ret_str = System.getProperty(propName, null); if(ret_str!=null) return ret_str; } catch (Exception e) { ; } // 見つからなかった場合はsha.propertiesファイルを試みる if(sha_properties==null) { //try to load sha_propreties file FileInputStream prop_file = null; // ある可能性のある二つの場所で探す // (1).\sha.properties (2)<java.home>\lib\sha.properties String path = ""; for (int i = 0; i <= 1; i++) { // sha.propertiesファイルを開く try { prop_file = new FileInputStream(path + "sha.properties"); // onewire.propertiesを読む try { sha_properties = new Properties(); sha_properties.load(prop_file); } catch (Exception e) { // 失敗 sha_properties = null; } } catch (IOException e) { prop_file = null; } // プロパティが読み込まれたかどうかをチェックする if (sha_properties != null) break; // 二番目のパスを試みる path = System.getProperty("java.home") + File.separator + "lib" + File.separator; } } if(sha_properties==null) { IOHelper.writeLine("Can't find sha.properties file"); return null; } else { Object ret = sha_properties.get(propName); if(ret==null) return null; else return ret.toString(); } } public static String getProperty (String propName, String defValue) { String ret = getProperty(propName); return (ret==null) ? defValue : ret; } public static boolean getPropertyBoolean(String propName, boolean defValue) { String strValue = getProperty(propName); if(strValue!=null) defValue = Boolean.valueOf(strValue).booleanValue(); return defValue; } public static byte[] getPropertyBytes(String propName, byte[] defValue) { String strValue = getProperty(propName); if(strValue!=null) { //only supports up to 128 bytes of data byte[] tmp = new byte[128]; //split the string on commas and spaces StringTokenizer strtok = new StringTokenizer(strValue,", "); //how many bytes we got int i = 0; while(strtok.hasMoreElements()) { //this string could have more than one byte in it String multiByteStr = strtok.nextToken(); int strLen = multiByteStr.length(); for(int j=0; j<strLen; j+=2) { //get just two nibbles at a time String byteStr = multiByteStr.substring(j, Math.min(j+2, strLen)); long lng = 0; try { //parse the two nibbles into a byte lng = Long.parseLong(byteStr, 16); } catch(NumberFormatException nfe) { nfe.printStackTrace(); //no mercy! return defValue; } //store the byte and increment the counter if(i<tmp.length) tmp[i++] = (byte)(lng&0x0FF); } } if(i>0) { byte[] retVal = new byte[i]; System.arraycopy(tmp, 0, retVal, 0, i); return retVal; } } return defValue; } } |
AuthenticationHost.java |
---|
import java.io.*; import java.net.*; import java.util.*; import com.dalsemi.onewire.*; import com.dalsemi.onewire.adapter.*; import com.dalsemi.onewire.container.*; import com.dalsemi.onewire.utils.*; import com.dalsemi.onewire.application.sha.*; /** * 新しい接続サービスを行うクラス * 同一スレッド上で動作する */ public class AuthenticationHost implements Runnable { /** * バイト配列に0xFFを入れる */ private static final byte[] ffBlock = new byte[] { (byte)0x0FF,(byte)0x0FF,(byte)0x0FF,(byte)0x0FF, (byte)0x0FF,(byte)0x0FF,(byte)0x0FF,(byte)0x0FF, (byte)0x0FF,(byte)0x0FF,(byte)0x0FF,(byte)0x0FF, (byte)0x0FF,(byte)0x0FF,(byte)0x0FF,(byte)0x0FF, (byte)0x0FF,(byte)0x0FF,(byte)0x0FF,(byte)0x0FF, (byte)0x0FF,(byte)0x0FF,(byte)0x0FF,(byte)0x0FF, (byte)0x0FF,(byte)0x0FF,(byte)0x0FF,(byte)0x0FF, (byte)0x0FF,(byte)0x0FF,(byte)0x0FF,(byte)0x0FF }; /** * サービスをしている接続 */ private AuthenticationConstants.Connection conn; /** * 接続確認のための乱数 */ Random rand; /** * 接続に対する暗号 */ String secret; /** * リモート認証のためのコプロセッサ情報 */ SHAiButtonCopr copr; /** * 接続のためのサーバーソケット */ ServerSocket serverSock; /** * ソケットサービスに対するコンストラクタ * 入出力ストリームを作り、クライアントにこのホストの * バージョンを送る */ public AuthenticationHost(SHAiButtonCopr copr, int listenPort, String secret) throws IOException { // チャレンジに対する乱数オブジェクト rand = new Random(); // 接続に対して暗号を設定する this.secret = secret; // 認証に対するコプロセッサのデータ this.copr = copr; // 接続のためのソケット serverSock = new ServerSocket(listenPort); } public void run() { byte[] SNum = new byte[8]; byte[] data = new byte[7]; try { for(;;) { Socket sock = null; sock = serverSock.accept(); // ソケットのtimeoutを10秒に設定する sock.setSoTimeout(10000); // 接続オブジェクトを作る conn = new AuthenticationConstants.Connection(); conn.sock = sock; conn.input = new DataInputStream(conn.sock.getInputStream()); conn.output = new DataOutputStream(new BufferedOutputStream( conn.sock.getOutputStream())); // クライアントを認証する byte[] chlg = new byte[8]; rand.nextBytes(chlg); conn.output.write(chlg); conn.output.flush(); // 暗号とチャレンジのcrcを計算する int crc = CRC16.compute(secret.getBytes(), 0); crc = CRC16.compute(chlg, crc); int answer = conn.input.readInt(); if(answer!=crc) { conn.output.writeByte(AuthenticationConstants.RET_FAILURE); conn.output.writeUTF("Client Authentication Failed"); conn.output.flush(); throw new IOException("authentication failed"); } else { conn.output.writeByte(AuthenticationConstants.RET_SUCCESS); conn.output.flush(); } if(authenticate(SNum,data)) { System.out.println(); System.out.println("Remote Authentication Successful for part: "); IOHelper.writeBytesHex(SNum,0,8); System.out.println("With the following verification data:"); IOHelper.writeBytes(data); System.out.println(); } else { System.out.println("Remote Authentication was " + "unseccessful for part: "); IOHelper.writeBytesHex(SNum,0,8); } } } catch(Exception ioe) { if(AuthenticationConstants.DEBUG) ioe.printStackTrace(); } } /** * Run method for socket Servicer. */ public boolean authenticate(byte[] SNum, byte[] data) throws OneWireException, OneWireIOException, IOException { byte[] accountData = new byte[32]; byte[] fullBindCode = new byte[15]; byte[] ver_data = new byte[7]; byte[] mac = new byte[20]; byte[] coprBindCode = new byte[7]; byte[] coprBindData = new byte[32]; byte lastError = 0x00; byte authCmd = 0x00; boolean authenticated = false; try { while(conn.sock!=null) { byte cmd = 0x00; cmd = conn.input.readByte(); if(cmd == AuthenticationConstants.START_OF_AUTH) { // get the bytes to block conn.input.read(SNum, 0, 8); // send the bind code coprBindCode = copr.getBindCode(); conn.output.write(coprBindCode); conn.output.flush(); coprBindData = copr.getBindData(); conn.output.write(coprBindData); conn.output.flush(); // Send the file name and file name extension of the file byte[] fileName = new byte[4]; byte fileNameExt; copr.getFilename(fileName,0); fileNameExt = copr.getFilenameExt(); conn.output.write(fileName); conn.output.flush(); conn.output.write(fileNameExt); conn.output.flush(); if(SNum[0] == 0x18) { authCmd = OneWireContainer18.VALIDATE_DATA_PAGE; } else if((SNum[0] == 0x33) || (SNum[0] == 0xB3)) { authCmd = OneWireContainer18.AUTH_HOST; } // Verifing the User lastError = verifyUserNet(copr, authCmd); // verify Transaction Data if((verifyTransactionDataNet(copr, data, SNum) == 0x00) && (lastError == 0x00)) { authenticated = true; } conn.output.writeByte(AuthenticationConstants.END_OF_AUTH); conn.output.flush(); conn.sock.close(); } } } catch(IOException ioe) { if(AuthenticationConstants.DEBUG) ioe.printStackTrace(); conn.sock.close(); } return authenticated; } private byte verifyUserNet(SHAiButtonCopr copr, byte authCmd) throws OneWireException, OneWireIOException, IOException { byte lastError = 0x00; int wcc = 0; byte[] chlg = new byte[3]; byte[] accountData = new byte[32]; byte[] mac = new byte[20]; byte[] fullBindCode = new byte[15]; byte[] coprBindCode = new byte[7]; byte[] coprBindData = new byte[32]; byte[] scratchpad = new byte[32]; if(!copr.generateChallenge(0, chlg, 0)) { lastError = AuthenticationConstants.COPR_COMPUTE_CHALLENGE_FAILED; } conn.output.writeByte(AuthenticationConstants.CHALLENGE_FOR_VERIFICATION); conn.output.flush(); conn.output.write(chlg,0,chlg.length); conn.output.flush(); conn.output.writeByte(AuthenticationConstants.VERIFY_USER_DATA); conn.output.flush(); wcc = conn.input.readInt(); conn.input.read(accountData,0,accountData.length); conn.input.read(mac,0,mac.length); conn.input.read(fullBindCode,0,fullBindCode.length); if(wcc<0) { System.arraycopy(ffBlock, 0, scratchpad, 8, 4); } else { //スクラッチパッドにwrite cycleカウンターをコピーする Convert.toByteArray(wcc, scratchpad, 8, 4); } //fullBindCodeからユーザアドレスとpage numを得る System.arraycopy(fullBindCode, 4, scratchpad, 12, 8); // 同じチャレンジバイトをセットする System.arraycopy(chlg, 0, scratchpad, 20, 3); if(!copr.verifyAuthentication(fullBindCode, accountData, scratchpad, mac, authCmd)) { lastError = AuthenticationConstants.COPROCESSOR_FAILURE; } return lastError; } private byte verifyTransactionDataNet(SHAiButtonCopr copr, byte[] ver_data, byte[] SNum) throws OneWireException, OneWireIOException, IOException { byte lastError = 0x00; int wcc = 0; byte[] accountData = new byte[32]; byte[] ver_mac = new byte[20]; byte[] scratchpad = new byte[32]; conn.output.writeByte(AuthenticationConstants.VERIFY_TRANSACTION_DATA); conn.output.flush(); conn.input.read(accountData,0,accountData.length); wcc = conn.input.readInt(); // ユーザの検証データを得る System.arraycopy(accountData,22,ver_data,0,7); // account data pageからmacを得る System.arraycopy(accountData, 2, ver_mac, 0, 20); // macをリセットする copr.getInitialSignature(accountData, 2); // CRCをリセットする accountData[30] = (byte)0; accountData[31] = (byte)0; // now we also need to get things like wcc, //user_page_number, user ID if(wcc<0) { // write cycleカウンターを持っていない System.arraycopy(ffBlock, 0, scratchpad, 8, 4); } else { // write cycleカウンターをスクラッチパッドにコピーする Convert.toByteArray(wcc, scratchpad, 8, 4); } scratchpad[12] = (byte)conn.input.readInt(); System.arraycopy(SNum,0,scratchpad,13,7); copr.getSigningChallenge(scratchpad, 20); if(!copr.verifySignature(accountData, scratchpad, ver_mac)) { lastError = AuthenticationConstants.COPROCESSOR_FAILURE; } return lastError; } } |
AuthenticationConstants.java |
---|
public interface AuthenticationConstants { /** Debug メッセージ・フラグ */ static final boolean DEBUG = false; /** TCP/IP接続のデフォールトのポート */ static final int DEFAULT_PORT = 6161; /** サーバーでの認証に対するデフォールトの暗証 */ static final String DEFAULT_SECRET = "Adapter Secret Default"; /*--------------------------------*/ /*----- メソッドが返すコード -----*/ /*--------------------------------*/ static final byte RET_SUCCESS = (byte)0xFF; static final byte RET_FAILURE = (byte)0xF0; /** ネットワークを通って送られるコマンド */ static final byte NO_ACTION = 0x00; static final byte START_OF_AUTH = (byte) 0xAA; static final byte END_OF_AUTH = (byte) 0xA0; static final byte CHALLENGE_FOR_VERIFICATION = (byte) 0xA1; static final byte VERIFY_USER_DATA = (byte) 0xA2; static final byte VERIFY_TRANSACTION_DATA = (byte) 0xA3; /** 失敗 */ static final byte COPROCESSOR_FAILURE = (byte) 0xC0; static final byte COPR_COMPUTE_CHALLENGE_FAILED = (byte) 0xC1; /** I/O streamでソケット接続する場合の内部ユーティリティ */ public static final class Connection { /** socket to host */ public java.net.Socket sock = null; /** input stream from socket */ public java.io.DataInputStream input = null; /** output stream from socket */ public java.io.DataOutputStream output = null; } /** 基本的にNULLオブジェクトである空の接続のインスタンス */ public static final Connection EMPTY_CONNECTION = new Connection(); } |