package com.iiordanov.bssh.service;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import com.iiordanov.bssh.service.TerminalManager;
import com.iiordanov.bssh.service.TerminalManager.KeyHolder;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.madgag.ssh.android.authagent.AndroidAuthAgent;
import com.trilead.ssh2.signature.DSAPrivateKey;
import com.trilead.ssh2.signature.DSAPublicKey;
import com.trilead.ssh2.signature.DSASHA1Verify;
import com.trilead.ssh2.signature.DSASignature;
import com.trilead.ssh2.signature.RSAPrivateKey;
import com.trilead.ssh2.signature.RSAPublicKey;
import com.trilead.ssh2.signature.RSASHA1Verify;
import com.trilead.ssh2.signature.RSASignature;
public class AuthAgentService extends Service {
private static final String TAG = "ConnectBot.AuthAgentService";
protected TerminalManager manager;
final Lock lock = new ReentrantLock();
final Condition managerReady = lock.newCondition();
private ServiceConnection connection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
Log.d(TAG, "Terminal manager available! Hurrah");
manager = ((TerminalManager.TerminalBinder) service).getService();
lock.lock();
try {
managerReady.signal();
} finally {
lock.unlock();
}
}
public void onServiceDisconnected(ComponentName className) {
manager = null;
Log.d(TAG, "Terminal manager gone...");
}
};
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind() called");
bindService(new Intent(this, TerminalManager.class), connection, BIND_AUTO_CREATE);
return mBinder;
}
private final AndroidAuthAgent.Stub mBinder = new AndroidAuthAgent.Stub() {
public Map getIdentities() throws RemoteException {
Log.d(TAG, "getIdentities() called");
waitForTerminalManager();
Log.d(TAG, "getIdentities() manager.loadedKeypairs : " + manager.loadedKeypairs);
return sshEncodedPubKeysFrom(manager.loadedKeypairs);
}
public byte[] sign(byte[] publicKey, byte[] data) throws RemoteException {
Log.d(TAG, "sign() called");
waitForTerminalManager();
Object trileadKey = keyPairFor(publicKey);
Log.d(TAG, "sign() - signing keypair found : "+trileadKey);
if (trileadKey == null) {
return null;
}
if (trileadKey instanceof RSAPrivateKey) {
return sshEncodedSignatureFor(data, (RSAPrivateKey) trileadKey);
} else if (trileadKey instanceof DSAPrivateKey) {
return sshEncodedSignatureFor(data, (DSAPrivateKey) trileadKey);
}
return null;
}
private void waitForTerminalManager() throws RemoteException {
lock.lock();
try {
while (manager == null) {
Log.d(TAG, "Waiting for TerminalManager...");
managerReady.await();
}
} catch (InterruptedException e) {
throw new RemoteException();
} finally {
lock.unlock();
}
Log.d(TAG, "Got TerminalManager : "+manager);
}
private Map<String, byte[]> sshEncodedPubKeysFrom(Map<String, KeyHolder> keypairs) {
Map<String, byte[]> encodedPubKeysByName = new HashMap<String, byte[]>(keypairs.size());
for (Entry<String, KeyHolder> entry : keypairs.entrySet()) {
byte[] encodedKey = sshEncodedPubKeyFrom(entry.getValue().trileadKey);
if (encodedKey != null) {
encodedPubKeysByName.put(entry.getKey(), encodedKey);
}
}
return encodedPubKeysByName;
}
private byte[] sshEncodedPubKeyFrom(Object trileadKey) {
try {
if (trileadKey instanceof RSAPrivateKey) {
RSAPublicKey pubkey = ((RSAPrivateKey) trileadKey).getPublicKey();
return RSASHA1Verify.encodeSSHRSAPublicKey(pubkey);
} else if (trileadKey instanceof DSAPrivateKey) {
DSAPublicKey pubkey = ((DSAPrivateKey) trileadKey).getPublicKey();
return DSASHA1Verify.encodeSSHDSAPublicKey(pubkey);
}
} catch (IOException e) {
Log.e(TAG, "Couldn't encode " + trileadKey, e);
}
return null;
}
private byte[] sshEncodedSignatureFor(byte[] data, RSAPrivateKey trileadKey) {
try {
RSASignature signature = RSASHA1Verify.generateSignature(data, trileadKey);
return RSASHA1Verify.encodeSSHRSASignature(signature);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private byte[] sshEncodedSignatureFor(byte[] data, DSAPrivateKey dsaPrivateKey) {
DSASignature signature = DSASHA1Verify.generateSignature(data, dsaPrivateKey, new SecureRandom());
return DSASHA1Verify.encodeSSHDSASignature(signature);
}
private Object keyPairFor(byte[] publicKey) {
String nickname = manager.getKeyNickname(publicKey);
if (nickname == null) {
Log.w(TAG, "No key-pair found for public-key.");
return null;
}
// check manager.loadedKeypairs.get(nickname).bean.isConfirmUse() and promptForPubkeyUse(nickname) ?
return manager.getKey(nickname);
}
};
}