Mercurial > 510Connectbot
view src/com/five_ten_sg/connectbot/GeneratePubkeyActivity.java @ 394:24186858750f
add test buffer changed when input stream runs dry, similar to async test
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Thu, 18 Sep 2014 14:51:14 -0700 |
parents | b40bc65fa09a |
children |
line wrap: on
line source
/* * ConnectBot: simple, powerful, open-source SSH client for Android * Copyright 2007 Kenny Root, Jeffrey Sharkey * * 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. */ package com.five_ten_sg.connectbot; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.PublicKey; import com.five_ten_sg.connectbot.bean.PubkeyBean; import com.five_ten_sg.connectbot.util.EntropyDialog; import com.five_ten_sg.connectbot.util.EntropyView; import com.five_ten_sg.connectbot.util.OnEntropyGatheredListener; import com.five_ten_sg.connectbot.util.PubkeyDatabase; import com.five_ten_sg.connectbot.util.PubkeyUtils; import android.app.Activity; import android.app.Dialog; import android.app.ProgressDialog; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnFocusChangeListener; import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; import android.widget.RadioGroup; import android.widget.RadioGroup.OnCheckedChangeListener; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import ch.ethz.ssh2.crypto.SecureRandomFix; import ch.ethz.ssh2.signature.ECDSASHA2Verify; public class GeneratePubkeyActivity extends Activity implements OnEntropyGatheredListener { public final static String TAG = "ConnectBot.GeneratePubkeyActivity"; private static final int RSA_MINIMUM_BITS = 768; final static int DEFAULT_BITS = 2048; final static int DSA_BITS = 1024; final static int[] ECDSA_SIZES = ECDSASHA2Verify.getCurveSizes(); final static int ECDSA_DEFAULT_BITS = ECDSA_SIZES[0]; private LayoutInflater inflater = null; private EditText nickname; private RadioGroup keyTypeGroup; private SeekBar bitsSlider; private EditText bitsText; private CheckBox unlockAtStartup; private CheckBox confirmUse; private Button save; private Dialog entropyDialog; private ProgressDialog progress; private EditText password1, password2; private String keyType = PubkeyDatabase.KEY_TYPE_RSA; private int minBits = RSA_MINIMUM_BITS; private int bits = DEFAULT_BITS; private byte[] entropy; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.act_generatepubkey); nickname = (EditText) findViewById(R.id.nickname); keyTypeGroup = (RadioGroup) findViewById(R.id.key_type); bitsText = (EditText) findViewById(R.id.bits); bitsSlider = (SeekBar) findViewById(R.id.bits_slider); password1 = (EditText) findViewById(R.id.password1); password2 = (EditText) findViewById(R.id.password2); unlockAtStartup = (CheckBox) findViewById(R.id.unlock_at_startup); confirmUse = (CheckBox) findViewById(R.id.confirm_use); save = (Button) findViewById(R.id.save); inflater = LayoutInflater.from(this); nickname.addTextChangedListener(textChecker); password1.addTextChangedListener(textChecker); password2.addTextChangedListener(textChecker); keyTypeGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() { public void onCheckedChanged(RadioGroup group, int checkedId) { if (checkedId == R.id.rsa) { minBits = RSA_MINIMUM_BITS; bitsSlider.setEnabled(true); bitsSlider.setProgress(DEFAULT_BITS - minBits); bitsText.setText(String.valueOf(DEFAULT_BITS)); bitsText.setEnabled(true); keyType = PubkeyDatabase.KEY_TYPE_RSA; } else if (checkedId == R.id.dsa) { // DSA keys can only be 1024 bits bitsSlider.setEnabled(false); bitsSlider.setProgress(DSA_BITS - minBits); bitsText.setText(String.valueOf(DSA_BITS)); bitsText.setEnabled(false); keyType = PubkeyDatabase.KEY_TYPE_DSA; } else if (checkedId == R.id.ec) { minBits = ECDSA_DEFAULT_BITS; bitsSlider.setEnabled(true); bitsSlider.setProgress(ECDSA_DEFAULT_BITS - minBits); bitsText.setText(String.valueOf(ECDSA_DEFAULT_BITS)); bitsText.setEnabled(true); keyType = PubkeyDatabase.KEY_TYPE_EC; } } }); bitsSlider.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) { if (PubkeyDatabase.KEY_TYPE_EC.equals(keyType)) { bits = getClosestFieldSize(progress + minBits); seekBar.setProgress(bits - minBits); } else { // Stay evenly divisible by 8 because it looks nicer to have // 2048 than 2043 bits. final int ourProgress = progress - (progress % 8); bits = minBits + ourProgress; } bitsText.setText(String.valueOf(bits)); } public void onStartTrackingTouch(SeekBar seekBar) { // We don't care about the start. } public void onStopTrackingTouch(SeekBar seekBar) { // We don't care about the stop. } }); bitsText.setOnFocusChangeListener(new OnFocusChangeListener() { public void onFocusChange(View v, boolean hasFocus) { if (!hasFocus) { final boolean isEc = PubkeyDatabase.KEY_TYPE_EC.equals(keyType); try { bits = Integer.parseInt(bitsText.getText().toString()); if (bits < minBits) { bits = minBits; bitsText.setText(String.valueOf(bits)); } if (isEc) { bits = getClosestFieldSize(bits); } } catch (NumberFormatException nfe) { bits = isEc ? ECDSA_DEFAULT_BITS : DEFAULT_BITS; bitsText.setText(String.valueOf(bits)); } bitsSlider.setProgress(bits - minBits); } } }); save.setOnClickListener(new OnClickListener() { public void onClick(View view) { GeneratePubkeyActivity.this.save.setEnabled(false); GeneratePubkeyActivity.this.startEntropyGather(); } }); } private void checkEntries() { boolean allowSave = true; if (!password1.getText().toString().equals(password2.getText().toString())) allowSave = false; if (nickname.getText().length() == 0) allowSave = false; save.setEnabled(allowSave); } private void startEntropyGather() { final View entropyView = inflater.inflate(R.layout.dia_gatherentropy, null, false); ((EntropyView)entropyView.findViewById(R.id.entropy)).addOnEntropyGatheredListener(GeneratePubkeyActivity.this); entropyDialog = new EntropyDialog(GeneratePubkeyActivity.this, entropyView); entropyDialog.show(); } public void onEntropyGathered(byte[] entropy) { // For some reason the entropy dialog was aborted, exit activity if (entropy == null) { finish(); return; } this.entropy = entropy.clone(); int numSetBits = 0; for (int i = 0; i < 20; i++) numSetBits += measureNumberOfSetBits(this.entropy[i]); Log.d(TAG, "Entropy distribution=" + (int)(100.0 * numSetBits / 160.0) + "%"); Log.d(TAG, "entropy gathered; attemping to generate key..."); startKeyGen(); } private void startKeyGen() { progress = new ProgressDialog(GeneratePubkeyActivity.this); progress.setMessage(GeneratePubkeyActivity.this.getResources().getText(R.string.pubkey_generating)); progress.setIndeterminate(true); progress.setCancelable(false); progress.show(); Thread keyGenThread = new Thread(mKeyGen); keyGenThread.setName("KeyGen"); keyGenThread.start(); } final private Runnable mKeyGen = new Runnable() { public void run() { try { boolean encrypted = false; int tmpbits = bits; if (keyType == PubkeyDatabase.KEY_TYPE_DSA) tmpbits = DSA_BITS; SecureRandomFix random = new SecureRandomFix(); // Work around JVM bug random.nextInt(); random.setSeed(entropy); KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(keyType); keyPairGen.initialize(tmpbits, random); KeyPair pair = keyPairGen.generateKeyPair(); PrivateKey priv = pair.getPrivate(); PublicKey pub = pair.getPublic(); String secret = password1.getText().toString(); if (secret.length() > 0) encrypted = true; Log.d(TAG, "private: " + PubkeyUtils.formatKey(priv)); Log.d(TAG, "public: " + PubkeyUtils.formatKey(pub)); PubkeyBean pubkey = new PubkeyBean(); pubkey.setNickname(nickname.getText().toString()); pubkey.setType(keyType); pubkey.setPrivateKey(PubkeyUtils.getEncodedPrivate(priv, secret)); pubkey.setPublicKey(pub.getEncoded()); pubkey.setEncrypted(encrypted); pubkey.setStartup(unlockAtStartup.isChecked()); pubkey.setConfirmUse(confirmUse.isChecked()); PubkeyDatabase pubkeydb = new PubkeyDatabase(GeneratePubkeyActivity.this); pubkeydb.savePubkey(pubkey); pubkeydb.close(); } catch (Exception e) { Log.e(TAG, "Could not generate key pair"); e.printStackTrace(); } GeneratePubkeyActivity.this.runOnUiThread(new Runnable() { public void run() { progress.dismiss(); GeneratePubkeyActivity.this.finish(); } }); } }; final private TextWatcher textChecker = new TextWatcher() { public void afterTextChanged(Editable s) {} public void beforeTextChanged(CharSequence s, int start, int count, int after) {} public void onTextChanged(CharSequence s, int start, int before, int count) { checkEntries(); } }; private int measureNumberOfSetBits(byte b) { int numSetBits = 0; for (int i = 0; i < 8; i++) { if ((b & 1) == 1) numSetBits++; b >>= 1; } return numSetBits; } private int getClosestFieldSize(int bits) { int outBits = ECDSA_DEFAULT_BITS; int distance = Math.abs(bits - ECDSA_DEFAULT_BITS); for (int i = 1; i < ECDSA_SIZES.length; i++) { int thisDistance = Math.abs(bits - ECDSA_SIZES[i]); if (thisDistance < distance) { distance = thisDistance; outBits = ECDSA_SIZES[i]; } } return outBits; } }