Add sshauthentication-api v1 support
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2017 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
* Copyright (C) 2017 Christian Hagau <ach@hagau.se>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ssh;
|
||||
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
|
||||
import org.bouncycastle.bcpg.HashAlgorithmTags;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* AuthenticationData holds metadata pertaining to the signing of a
|
||||
* AuthenticationParcel via a AuthenticationOperation
|
||||
*/
|
||||
@AutoValue
|
||||
public abstract class AuthenticationData implements Parcelable {
|
||||
public abstract long getAuthenticationMasterKeyId();
|
||||
public abstract Long getAuthenticationSubKeyId();
|
||||
@Nullable
|
||||
public abstract List<Long> getAllowedAuthenticationKeyIds();
|
||||
|
||||
public abstract int getHashAlgorithm();
|
||||
|
||||
public static Builder builder() {
|
||||
return new AutoValue_AuthenticationData.Builder()
|
||||
.setAuthenticationMasterKeyId(Constants.key.none)
|
||||
.setAuthenticationSubKeyId(Constants.key.none)
|
||||
.setHashAlgorithm(HashAlgorithmTags.SHA512);
|
||||
}
|
||||
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
public abstract AuthenticationData build();
|
||||
|
||||
public abstract Builder setAuthenticationMasterKeyId(long authenticationMasterKeyId);
|
||||
public abstract Builder setAuthenticationSubKeyId(Long authenticationSubKeyId);
|
||||
|
||||
public abstract Builder setHashAlgorithm(int hashAlgorithm);
|
||||
|
||||
|
||||
abstract Builder setAllowedAuthenticationKeyIds(List<Long> allowedAuthenticationKeyIds);
|
||||
public Builder setAllowedAuthenticationKeyIds(Collection<Long> allowedAuthenticationKeyIds) {
|
||||
setAllowedAuthenticationKeyIds(Collections.unmodifiableList(new ArrayList<>(allowedAuthenticationKeyIds)));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Christian Hagau <ach@hagau.se>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ssh;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import org.bouncycastle.openpgp.PGPAuthenticationSignatureGenerator;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder;
|
||||
import org.sufficientlysecure.keychain.operations.BaseOperation;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
|
||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
|
||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.PassphraseCacheInterface;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||
import org.sufficientlysecure.keychain.provider.KeyRepository;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* This class supports a single, low-level, authentication operation.
|
||||
* <p/>
|
||||
* The operation of this class takes a AuthenticationParcel
|
||||
* as input, and signs the included challenge as parametrized in the
|
||||
* AuthenticationData object. It returns its status
|
||||
* and a possible signature as a AuthenticationResult.
|
||||
* <p/>
|
||||
*
|
||||
* @see AuthenticationParcel
|
||||
*/
|
||||
public class AuthenticationOperation extends BaseOperation<AuthenticationParcel> {
|
||||
|
||||
private static final String TAG = "AuthenticationOperation";
|
||||
|
||||
public AuthenticationOperation(Context context, KeyRepository keyRepository) {
|
||||
super(context, keyRepository, null);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public AuthenticationResult execute(AuthenticationParcel input,
|
||||
CryptoInputParcel cryptoInput) {
|
||||
return executeInternal(input.getAuthenticationData(), cryptoInput, input);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public AuthenticationResult execute(AuthenticationData data,
|
||||
CryptoInputParcel cryptoInput,
|
||||
AuthenticationParcel authenticationParcel) {
|
||||
return executeInternal(data, cryptoInput, authenticationParcel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs challenge based on given parameters
|
||||
*/
|
||||
private AuthenticationResult executeInternal(AuthenticationData data,
|
||||
CryptoInputParcel cryptoInput,
|
||||
AuthenticationParcel authenticationParcel) {
|
||||
int indent = 0;
|
||||
OperationLog log = new OperationLog();
|
||||
|
||||
log.add(LogType.MSG_AUTH, indent);
|
||||
indent += 1;
|
||||
|
||||
Log.d(TAG, data.toString());
|
||||
|
||||
long opTime;
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
byte[] signature;
|
||||
|
||||
byte[] challenge = authenticationParcel.getChallenge();
|
||||
|
||||
int hashAlgorithm = data.getHashAlgorithm();
|
||||
|
||||
long authMasterKeyId = data.getAuthenticationMasterKeyId();
|
||||
Long authSubKeyId = data.getAuthenticationSubKeyId();
|
||||
if (authSubKeyId == null) {
|
||||
try { // Get the key id of the authentication key belonging to the master key id
|
||||
authSubKeyId = mKeyRepository.getCachedPublicKeyRing(authMasterKeyId).getSecretAuthenticationId();
|
||||
} catch (PgpKeyNotFoundException e) {
|
||||
log.add(LogType.MSG_AUTH_ERROR_KEY_AUTH, indent);
|
||||
return new AuthenticationResult(AuthenticationResult.RESULT_ERROR, log);
|
||||
}
|
||||
}
|
||||
|
||||
// Get keyring with the authentication key
|
||||
CanonicalizedSecretKeyRing authKeyRing;
|
||||
try {
|
||||
authKeyRing = mKeyRepository.getCanonicalizedSecretKeyRing(authMasterKeyId);
|
||||
} catch (KeyRepository.NotFoundException e) {
|
||||
log.add(LogType.MSG_AUTH_ERROR_KEY_AUTH, indent);
|
||||
return new AuthenticationResult(AuthenticationResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
CanonicalizedSecretKey authKey = authKeyRing.getSecretKey(authSubKeyId);
|
||||
|
||||
// Make sure the client is allowed to access this key
|
||||
Collection<Long> allowedAuthenticationKeyIds = data.getAllowedAuthenticationKeyIds();
|
||||
if (allowedAuthenticationKeyIds != null && !allowedAuthenticationKeyIds.contains(authMasterKeyId)) {
|
||||
// this key is in our db, but NOT allowed!
|
||||
log.add(LogType.MSG_AUTH_ERROR_KEY_NOT_ALLOWED, indent + 1);
|
||||
return new AuthenticationResult(AuthenticationResult.RESULT_KEY_DISALLOWED, log);
|
||||
}
|
||||
|
||||
// Make sure key is not expired or revoked
|
||||
if (authKeyRing.isExpired() || authKeyRing.isRevoked()
|
||||
|| authKey.isExpired() || authKey.isRevoked()) {
|
||||
log.add(LogType.MSG_AUTH_ERROR_REVOKED_OR_EXPIRED, indent);
|
||||
return new AuthenticationResult(AuthenticationResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
// Make sure the selected key is allowed to authenticate
|
||||
if (!authKey.canAuthenticate()) {
|
||||
log.add(LogType.MSG_AUTH_ERROR_KEY_AUTH, indent);
|
||||
return new AuthenticationResult(AuthenticationResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
CanonicalizedSecretKey.SecretKeyType secretKeyType;
|
||||
try {
|
||||
secretKeyType = mKeyRepository
|
||||
.getCachedPublicKeyRing(authMasterKeyId)
|
||||
.getSecretKeyType(authSubKeyId);
|
||||
} catch (KeyRepository.NotFoundException e) {
|
||||
log.add(LogType.MSG_AUTH_ERROR_KEY_AUTH, indent);
|
||||
return new AuthenticationResult(AuthenticationResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
switch (secretKeyType) {
|
||||
case DIVERT_TO_CARD:
|
||||
case PASSPHRASE_EMPTY: {
|
||||
boolean isUnlocked;
|
||||
try {
|
||||
isUnlocked = authKey.unlock(new Passphrase());
|
||||
} catch (PgpGeneralException e) {
|
||||
log.add(LogType.MSG_AUTH_ERROR_UNLOCK, indent);
|
||||
return new AuthenticationResult(AuthenticationResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
if (!isUnlocked) {
|
||||
throw new AssertionError(
|
||||
"PASSPHRASE_EMPTY/DIVERT_TO_CARD keyphrase not unlocked with empty passphrase."
|
||||
+ " This is a programming error!");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PASSPHRASE: {
|
||||
Passphrase localPassphrase = cryptoInput.getPassphrase();
|
||||
if (localPassphrase == null) {
|
||||
try {
|
||||
localPassphrase = getCachedPassphrase(authMasterKeyId, authKey.getKeyId());
|
||||
} catch (PassphraseCacheInterface.NoSecretKeyException ignored) {
|
||||
}
|
||||
}
|
||||
if (localPassphrase == null) {
|
||||
log.add(LogType.MSG_AUTH_PENDING_PASSPHRASE, indent + 1);
|
||||
return new AuthenticationResult(log,
|
||||
RequiredInputParcel.createRequiredAuthenticationPassphrase(
|
||||
authMasterKeyId, authKey.getKeyId()),
|
||||
cryptoInput);
|
||||
}
|
||||
|
||||
boolean isUnlocked;
|
||||
try {
|
||||
isUnlocked = authKey.unlock(localPassphrase);
|
||||
} catch (PgpGeneralException e) {
|
||||
log.add(LogType.MSG_AUTH_ERROR_UNLOCK, indent);
|
||||
return new AuthenticationResult(AuthenticationResult.RESULT_ERROR, log);
|
||||
}
|
||||
if (!isUnlocked) {
|
||||
log.add(LogType.MSG_AUTH_ERROR_BAD_PASSPHRASE, indent);
|
||||
return new AuthenticationResult(AuthenticationResult.RESULT_ERROR, log);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GNU_DUMMY: {
|
||||
log.add(LogType.MSG_AUTH_ERROR_UNLOCK, indent);
|
||||
return new AuthenticationResult(AuthenticationResult.RESULT_ERROR, log);
|
||||
}
|
||||
default: {
|
||||
throw new AssertionError("Unhandled SecretKeyType! (should not happen)");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PGPAuthenticationSignatureGenerator signatureGenerator;
|
||||
try {
|
||||
signatureGenerator = authKey.getAuthenticationSignatureGenerator(
|
||||
hashAlgorithm, cryptoInput.getCryptoData());
|
||||
} catch (PgpGeneralException e) {
|
||||
log.add(LogType.MSG_AUTH_ERROR_NFC, indent);
|
||||
return new AuthenticationResult(AuthenticationResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
signatureGenerator.update(challenge, 0, challenge.length);
|
||||
|
||||
try {
|
||||
signature = signatureGenerator.generate().getSignature();
|
||||
} catch (NfcSyncPGPContentSignerBuilder.NfcInteractionNeeded e) {
|
||||
// this secret key diverts to a OpenPGP card, thus requires user interaction
|
||||
log.add(LogType.MSG_AUTH_PENDING_NFC, indent);
|
||||
return new AuthenticationResult(log, RequiredInputParcel.createSecurityTokenAuthenticationOperation(
|
||||
authKey.getRing().getMasterKeyId(), authKey.getKeyId(),
|
||||
e.hashToSign, e.hashAlgo), cryptoInput);
|
||||
} catch (PGPException e) {
|
||||
log.add(LogType.MSG_AUTH_ERROR_SIG, indent);
|
||||
return new AuthenticationResult(AuthenticationResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
opTime = System.currentTimeMillis() - startTime;
|
||||
Log.d(TAG, "Authentication operation duration : " + String.format("%.2f", opTime / 1000.0) + "s");
|
||||
|
||||
log.add(LogType.MSG_AUTH_OK, indent);
|
||||
AuthenticationResult result = new AuthenticationResult(AuthenticationResult.RESULT_OK, log);
|
||||
|
||||
result.setSignature(signature);
|
||||
result.mOperationTime = opTime;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
* Copyright (C) 2017 Christian Hagau <ach@hagau.se>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ssh;
|
||||
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
|
||||
/**
|
||||
* AuthenticationParcel holds the challenge to be signed for authentication
|
||||
*/
|
||||
@AutoValue
|
||||
public abstract class AuthenticationParcel implements Parcelable {
|
||||
|
||||
public abstract AuthenticationData getAuthenticationData();
|
||||
|
||||
@SuppressWarnings("mutable")
|
||||
public abstract byte[] getChallenge();
|
||||
|
||||
public static AuthenticationParcel createAuthenticationParcel(AuthenticationData authenticationData,
|
||||
byte[] challenge) {
|
||||
return new AutoValue_AuthenticationParcel(authenticationData, challenge);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
* Copyright (C) 2017 Christian Hagau <ach@hagau.se>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ssh;
|
||||
|
||||
import android.os.Parcel;
|
||||
|
||||
import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||
|
||||
/**
|
||||
* AuthenticationResult holds the result of a AuthenticationOperation
|
||||
*/
|
||||
public class AuthenticationResult extends InputPendingResult {
|
||||
|
||||
public static final int RESULT_KEY_DISALLOWED = RESULT_ERROR + 32;
|
||||
|
||||
byte[] mSignature;
|
||||
public long mOperationTime;
|
||||
|
||||
public void setSignature(byte[] signature) {
|
||||
mSignature = signature;
|
||||
}
|
||||
|
||||
public byte[] getSignature() {
|
||||
return mSignature;
|
||||
}
|
||||
|
||||
public AuthenticationResult(int result, OperationLog log) {
|
||||
super(result, log);
|
||||
}
|
||||
|
||||
public AuthenticationResult(OperationLog log, RequiredInputParcel requiredInput,
|
||||
CryptoInputParcel cryptoInputParcel) {
|
||||
super(log, requiredInput, cryptoInputParcel);
|
||||
}
|
||||
|
||||
public AuthenticationResult(Parcel source) {
|
||||
super(source);
|
||||
mSignature = source.readInt() != 0 ? source.createByteArray() : null;
|
||||
}
|
||||
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
if (mSignature != null) {
|
||||
dest.writeInt(1);
|
||||
dest.writeByteArray(mSignature);
|
||||
} else {
|
||||
dest.writeInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
public static final Creator<AuthenticationResult> CREATOR = new Creator<AuthenticationResult>() {
|
||||
public AuthenticationResult createFromParcel(final Parcel source) {
|
||||
return new AuthenticationResult(source);
|
||||
}
|
||||
|
||||
public AuthenticationResult[] newArray(final int size) {
|
||||
return new AuthenticationResult[size];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Christian Hagau <ach@hagau.se>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ssh.key;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class SshDSAPublicKey extends SshPublicKey {
|
||||
public static final String KEY_ID = "ssh-dss";
|
||||
|
||||
private BigInteger mP;
|
||||
private BigInteger mQ;
|
||||
private BigInteger mG;
|
||||
private BigInteger mY;
|
||||
|
||||
public SshDSAPublicKey(BigInteger p, BigInteger q, BigInteger g, BigInteger y) {
|
||||
super(KEY_ID);
|
||||
mP = p;
|
||||
mQ = q;
|
||||
mG = g;
|
||||
mY = y;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void putData(SshEncodedData data) {
|
||||
data.putMPInt(mP);
|
||||
data.putMPInt(mQ);
|
||||
data.putMPInt(mG);
|
||||
data.putMPInt(mY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Christian Hagau <ach@hagau.se>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ssh.key;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class SshECDSAPublicKey extends SshPublicKey {
|
||||
public static final String KEY_ID = "ecdsa-sha2-";
|
||||
|
||||
private BigInteger mQ;
|
||||
|
||||
private String mCurve;
|
||||
|
||||
public SshECDSAPublicKey(String curve, BigInteger q) {
|
||||
super(KEY_ID + curve);
|
||||
mCurve = curve;
|
||||
mQ = q;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void putData(SshEncodedData data) {
|
||||
data.putString(mCurve);
|
||||
data.putString(mQ.toByteArray());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Christian Hagau <ach@hagau.se>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ssh.key;
|
||||
|
||||
public class SshEd25519PublicKey extends SshPublicKey {
|
||||
public static final String KEY_ID = "ssh-ed25519";
|
||||
|
||||
private byte[] mAbyte;
|
||||
|
||||
public SshEd25519PublicKey(byte[] aByte) {
|
||||
super(KEY_ID);
|
||||
mAbyte = aByte;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void putData(SshEncodedData data) {
|
||||
data.putString(mAbyte);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Christian Hagau <ach@hagau.se>
|
||||
*
|
||||
* 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.sufficientlysecure.keychain.ssh.key;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class SshEncodedData {
|
||||
private ByteArrayOutputStream mData;
|
||||
|
||||
public SshEncodedData() {
|
||||
this(64);
|
||||
}
|
||||
|
||||
public SshEncodedData(int initialLength) {
|
||||
mData = new ByteArrayOutputStream(initialLength);
|
||||
}
|
||||
|
||||
public void putString(String string) {
|
||||
byte[] buffer = string.getBytes();
|
||||
putString(buffer);
|
||||
}
|
||||
|
||||
public void putString(byte[] buffer) {
|
||||
putUInt32(buffer.length);
|
||||
mData.write(buffer, 0, buffer.length);
|
||||
}
|
||||
|
||||
public void putMPInt(BigInteger mpInt) {
|
||||
byte buffer[] = mpInt.toByteArray();
|
||||
if ((buffer.length == 1) && (buffer[0] == 0)) {
|
||||
putUInt32(0);
|
||||
} else {
|
||||
putString(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public void putUInt32(int uInt) {
|
||||
mData.write(uInt >> 24);
|
||||
mData.write(uInt >> 16);
|
||||
mData.write(uInt >> 8);
|
||||
mData.write(uInt);
|
||||
}
|
||||
|
||||
public void putByte(byte octet) {
|
||||
mData.write(octet);
|
||||
}
|
||||
|
||||
public void putBoolean(boolean flag) {
|
||||
if (flag) {
|
||||
mData.write(1);
|
||||
} else {
|
||||
mData.write(0);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
|
||||
return mData.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Christian Hagau <ach@hagau.se>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ssh.key;
|
||||
|
||||
import android.util.Base64;
|
||||
|
||||
public abstract class SshPublicKey {
|
||||
protected SshEncodedData mData;
|
||||
|
||||
private String mKeyType;
|
||||
|
||||
public SshPublicKey(String keytype) {
|
||||
mData = new SshEncodedData();
|
||||
mKeyType = keytype;
|
||||
mData.putString(mKeyType);
|
||||
}
|
||||
|
||||
protected abstract void putData(SshEncodedData data);
|
||||
|
||||
public String getPublicKeyBlob() {
|
||||
String publicKeyBlob = "";
|
||||
publicKeyBlob += mKeyType + " ";
|
||||
|
||||
putData(mData);
|
||||
|
||||
String keyBlob = Base64.encodeToString(mData.getBytes(), Base64.NO_WRAP);
|
||||
publicKeyBlob += keyBlob;
|
||||
|
||||
return publicKeyBlob;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Christian Hagau <ach@hagau.se>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ssh.key;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class SshRSAPublicKey extends SshPublicKey {
|
||||
public static final String KEY_ID = "ssh-rsa";
|
||||
|
||||
private BigInteger mExponent;
|
||||
private BigInteger mModulus;
|
||||
|
||||
public SshRSAPublicKey(BigInteger exponent, BigInteger modulus) {
|
||||
super(KEY_ID);
|
||||
mExponent = exponent;
|
||||
mModulus = modulus;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void putData(SshEncodedData data) {
|
||||
data.putMPInt(mExponent);
|
||||
data.putMPInt(mModulus);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user