226 lines
7.5 KiB
Java
226 lines
7.5 KiB
Java
/*
|
|
* Copyright (C) 2017 Christian Hagau <ach@hagau.se>
|
|
* Copyright (C) 2017 Michael Perk
|
|
*
|
|
* 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 org.openintents.ssh.authentication;
|
|
|
|
import android.annotation.TargetApi;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.os.AsyncTask;
|
|
import android.os.Build;
|
|
import android.util.Log;
|
|
|
|
public class SshAuthenticationApi {
|
|
private static final String TAG = "SshAuthenticationApi";
|
|
|
|
public static final String SERVICE_INTENT = "org.openintents.ssh.authentication.ISshAuthenticationService";
|
|
|
|
public static final String EXTRA_API_VERSION = "api_version";
|
|
public static final int API_VERSION = 1;
|
|
|
|
public static final String EXTRA_RESULT_CODE = "result_code";
|
|
public static final int RESULT_CODE_ERROR = 0;
|
|
public static final int RESULT_CODE_SUCCESS = 1;
|
|
public static final int RESULT_CODE_USER_INTERACTION_REQUIRED = 2;
|
|
|
|
/**
|
|
* ACTION_SIGN
|
|
*
|
|
* Sign a given challenge
|
|
*
|
|
* returns the encoded signature as described in
|
|
* RFC4253, RFC5656 & draft-ietf-curdle-ssh-ed25519
|
|
* and their respective updates
|
|
*
|
|
* required extras:
|
|
* String EXTRA_KEY_ID
|
|
* byte[] EXTRA_CHALLENGE
|
|
* int EXTRA_HASH_ALGORITHM
|
|
*
|
|
* returned extras:
|
|
* byte[] EXTRA_SIGNATURE
|
|
*
|
|
* Note: for EdDSA the hash algorithm is ignored, PureEdDSA has to be implemented
|
|
*/
|
|
public static final String ACTION_SIGN = "org.openintents.ssh.action.SIGN";
|
|
public static final String EXTRA_CHALLENGE = "challenge";
|
|
public static final String EXTRA_HASH_ALGORITHM = "hash_algorithm";
|
|
public static final String EXTRA_SIGNATURE = "signature";
|
|
|
|
/* hash algorithms used in signature generation */
|
|
public static final int SHA1 = 0;
|
|
public static final int SHA224 = 1;
|
|
public static final int SHA256 = 2;
|
|
public static final int SHA384 = 3;
|
|
public static final int SHA512 = 4;
|
|
public static final int RIPEMD160 = 5;
|
|
|
|
/**
|
|
* ACTION_SELECT_KEY
|
|
*
|
|
* Select a key
|
|
*
|
|
* returned extras:
|
|
* String EXTRA_KEY_ID
|
|
* String EXTRA_KEY_DESCRIPTION
|
|
*/
|
|
public static final String ACTION_SELECT_KEY = "org.openintents.ssh.action.SELECT_KEY";
|
|
public static final String EXTRA_KEY_DESCRIPTION = "key_description";
|
|
|
|
/**
|
|
* ACTION_GET_PUBLIC_KEY
|
|
*
|
|
* Get the public key for a key
|
|
*
|
|
* returns the public key encoded according to the ASN.1 type
|
|
* 'SubjectPublicKeyInfo' as defined in the X.509 standard,
|
|
* see RFC5280, RFC3279 and draft-ietf-curdle-pkix
|
|
* and their respective updates
|
|
*
|
|
* required extras:
|
|
* String EXTRA_KEY_ID
|
|
*
|
|
* returned extras:
|
|
* byte[] EXTRA_PUBLIC_KEY
|
|
* int EXTRA_PUBLIC_KEY_ALGORITHM
|
|
*/
|
|
public static final String ACTION_GET_PUBLIC_KEY = "org.openintents.ssh.action.GET_PUBLIC_KEY";
|
|
public static final String EXTRA_PUBLIC_KEY = "public_key";
|
|
public static final String EXTRA_PUBLIC_KEY_ALGORITHM = "public_key_algorithm";
|
|
|
|
/* public key algorithms */
|
|
public static final int RSA = 0;
|
|
public static final int ECDSA = 1;
|
|
public static final int EDDSA = 2;
|
|
public static final int DSA = 3;
|
|
|
|
/**
|
|
* ACTION_GET_SSH_PUBLIC_KEY
|
|
*
|
|
* Get the SSH public key for a key
|
|
*
|
|
* returns the public key in SSH public key format,
|
|
* as described in RFC4253, RFC5656 and draft-ietf-curdle-ssh-ed25519
|
|
* and their respective updates
|
|
*
|
|
* required extras:
|
|
* String EXTRA_KEY_ID
|
|
*
|
|
* returned extras:
|
|
* String EXTRA_SSH_PUBLIC_KEY
|
|
*/
|
|
public static final String ACTION_GET_SSH_PUBLIC_KEY = "org.openintents.ssh.action.GET_SSH_PUBLIC_KEY";
|
|
public static final String EXTRA_SSH_PUBLIC_KEY = "ssh_public_key";
|
|
|
|
/**
|
|
* Error result of type SshAuthenticationApiError
|
|
*/
|
|
public static final String EXTRA_ERROR = "error";
|
|
|
|
/**
|
|
* Pending Intent requiring user interaction
|
|
*/
|
|
public static final String EXTRA_PENDING_INTENT = "intent";
|
|
|
|
/**
|
|
* Key identifier
|
|
*/
|
|
public static final String EXTRA_KEY_ID = "key_id";
|
|
|
|
|
|
private final ISshAuthenticationService mService;
|
|
private final Context mContext;
|
|
|
|
|
|
public SshAuthenticationApi(Context context, ISshAuthenticationService service) {
|
|
mService = service;
|
|
mContext = context;
|
|
}
|
|
|
|
public interface ISshAgentCallback {
|
|
void onReturn(final Intent result);
|
|
}
|
|
|
|
private class SshAgentAsyncTask extends AsyncTask<Void, Void, Intent> {
|
|
Intent mRequest;
|
|
ISshAgentCallback mCallback;
|
|
|
|
private SshAgentAsyncTask(Intent request, ISshAgentCallback callback) {
|
|
mRequest = request;
|
|
mCallback = callback;
|
|
}
|
|
|
|
@Override
|
|
protected Intent doInBackground(Void... unused) {
|
|
return executeApi(mRequest);
|
|
}
|
|
|
|
protected void onPostExecute(Intent result) {
|
|
mCallback.onReturn(result);
|
|
}
|
|
|
|
}
|
|
|
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
|
public void executeApiAsync(Intent data, ISshAgentCallback callback) {
|
|
SshAgentAsyncTask task = new SshAgentAsyncTask(data, callback);
|
|
|
|
// don't serialize async tasks, always execute them in parallel
|
|
// http://commonsware.com/blog/2012/04/20/asynctask-threading-regression-confirmed.html
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
|
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
} else {
|
|
task.execute();
|
|
}
|
|
}
|
|
|
|
public Intent executeApi(Intent data) {
|
|
try {
|
|
// always send version from client
|
|
data.putExtra(SshAuthenticationApi.EXTRA_API_VERSION, SshAuthenticationApi.API_VERSION);
|
|
|
|
Intent result;
|
|
|
|
// blocks until result is ready
|
|
result = mService.execute(data);
|
|
|
|
// set class loader to current context to allow unparcelling of SshAuthenticationApiError
|
|
// http://stackoverflow.com/a/3806769
|
|
result.setExtrasClassLoader(mContext.getClassLoader());
|
|
|
|
return result;
|
|
} catch (Exception e) {
|
|
Log.e(TAG, "Exception in executeApi call", e);
|
|
return createExceptionErrorResult(SshAuthenticationApiError.CLIENT_SIDE_ERROR,
|
|
"Exception in executeApi call", e);
|
|
}
|
|
}
|
|
|
|
private Intent createErrorResult(int errorCode, String errorMessage) {
|
|
Log.e(TAG, errorMessage);
|
|
Intent result = new Intent();
|
|
result.putExtra(SshAuthenticationApi.EXTRA_ERROR, new SshAuthenticationApiError(errorCode, errorMessage));
|
|
result.putExtra(SshAuthenticationApi.EXTRA_RESULT_CODE, SshAuthenticationApi.RESULT_CODE_ERROR);
|
|
return result;
|
|
}
|
|
|
|
private Intent createExceptionErrorResult(int errorCode, String errorMessage, Exception e) {
|
|
String message = errorMessage + " : " + e.getMessage();
|
|
return createErrorResult(errorCode, message);
|
|
}
|
|
}
|