open-keychain/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
2018-07-27 11:17:18 +02:00

334 lines
13 KiB
Java

/*
* Copyright (C) 2017 Schürmann & Breitmoser GbR
*
* 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.service;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import com.google.auto.value.AutoValue;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
import org.sufficientlysecure.keychain.util.Passphrase;
/**
* This class is a a transferable representation for a collection of changes
* to be done on a keyring.
* <p/>
* This class should include all types of operations supported in the backend.
* <p/>
* All changes are done in a differential manner. Besides the two key
* identification attributes, all attributes may be null, which indicates no
* change to the keyring. This is also the reason why boxed values are used
* instead of primitives in the subclasses.
* <p/>
* Application of operations in the backend should be fail-fast, which means an
* error in any included operation (for example revocation of a non-existent
* subkey) will cause the operation as a whole to fail.
*/
@AutoValue
public abstract class SaveKeyringParcel implements Parcelable {
// the master key id to be edited. if this is null, a new one will be created
@Nullable
public abstract Long getMasterKeyId();
// the key fingerprint, for safety. MUST be null for a new key.
@Nullable
@SuppressWarnings("mutable")
public abstract byte[] getFingerprint();
public abstract List<String> getAddUserIds();
public abstract List<WrappedUserAttribute> getAddUserAttribute();
public abstract List<SubkeyAdd> getAddSubKeys();
public abstract List<SubkeyChange> getChangeSubKeys();
@Nullable
public abstract String getChangePrimaryUserId();
public abstract List<String> getRevokeUserIds();
public abstract List<Long> getRevokeSubKeys();
// if these are non-null, PINs will be changed on the token
@Nullable
public abstract Passphrase getSecurityTokenPin();
@Nullable
public abstract Passphrase getSecurityTokenAdminPin();
public abstract boolean isShouldUpload();
public abstract boolean isShouldUploadAtomic();
@Nullable
public abstract HkpKeyserverAddress getUploadKeyserver();
@Nullable
public abstract ChangeUnlockParcel getNewUnlock();
public static Builder buildNewKeyringParcel() {
return new AutoValue_SaveKeyringParcel.Builder()
.setShouldUpload(false)
.setShouldUploadAtomic(false);
}
public static Builder buildChangeKeyringParcel(long masterKeyId, byte[] fingerprint) {
return buildNewKeyringParcel()
.setMasterKeyId(masterKeyId)
.setFingerprint(fingerprint);
}
abstract Builder toBuilder();
public static Builder buildUpon(SaveKeyringParcel saveKeyringParcel) {
SaveKeyringParcel.Builder builder = saveKeyringParcel.toBuilder();
builder.addUserIds.addAll(saveKeyringParcel.getAddUserIds());
builder.revokeUserIds.addAll(saveKeyringParcel.getRevokeUserIds());
builder.addUserAttribute.addAll(saveKeyringParcel.getAddUserAttribute());
builder.addSubKeys.addAll(saveKeyringParcel.getAddSubKeys());
builder.changeSubKeys.addAll(saveKeyringParcel.getChangeSubKeys());
builder.revokeSubKeys.addAll(saveKeyringParcel.getRevokeSubKeys());
return builder;
}
@AutoValue.Builder
public static abstract class Builder {
private ArrayList<String> addUserIds = new ArrayList<>();
private ArrayList<String> revokeUserIds = new ArrayList<>();
private ArrayList<WrappedUserAttribute> addUserAttribute = new ArrayList<>();
private ArrayList<SubkeyAdd> addSubKeys = new ArrayList<>();
private ArrayList<SubkeyChange> changeSubKeys = new ArrayList<>();
private ArrayList<Long> revokeSubKeys = new ArrayList<>();
public abstract Builder setChangePrimaryUserId(String changePrimaryUserId);
public abstract Builder setSecurityTokenPin(Passphrase securityTokenPin);
public abstract Builder setSecurityTokenAdminPin(Passphrase securityTokenAdminPin);
public abstract Builder setNewUnlock(ChangeUnlockParcel newUnlock);
public abstract Long getMasterKeyId();
public abstract byte[] getFingerprint();
public abstract String getChangePrimaryUserId();
public ArrayList<SubkeyAdd> getMutableAddSubKeys() {
return addSubKeys;
}
public ArrayList<String> getMutableAddUserIds() {
return addUserIds;
}
public ArrayList<Long> getMutableRevokeSubKeys() {
return revokeSubKeys;
}
public ArrayList<String> getMutableRevokeUserIds() {
return revokeUserIds;
}
abstract Builder setMasterKeyId(Long masterKeyId);
abstract Builder setFingerprint(byte[] fingerprint);
abstract Builder setAddUserIds(List<String> addUserIds);
abstract Builder setAddUserAttribute(List<WrappedUserAttribute> addUserAttribute);
abstract Builder setAddSubKeys(List<SubkeyAdd> addSubKeys);
abstract Builder setChangeSubKeys(List<SubkeyChange> changeSubKeys);
abstract Builder setRevokeUserIds(List<String> revokeUserIds);
abstract Builder setRevokeSubKeys(List<Long> revokeSubKeys);
abstract Builder setShouldUpload(boolean upload);
abstract Builder setShouldUploadAtomic(boolean uploadAtomic);
abstract Builder setUploadKeyserver(HkpKeyserverAddress keyserver);
public void setUpdateOptions(boolean upload, boolean uploadAtomic, HkpKeyserverAddress keyserver) {
setShouldUpload(upload);
setShouldUploadAtomic(uploadAtomic);
setUploadKeyserver(keyserver);
}
public void addSubkeyAdd(SubkeyAdd subkeyAdd) {
addSubKeys.add(subkeyAdd);
}
public void addUserId(String userId) {
addUserIds.add(userId);
}
public void addRevokeSubkey(long masterKeyId) {
revokeSubKeys.add(masterKeyId);
}
public void removeRevokeSubkey(long keyId) {
revokeSubKeys.remove(keyId);
}
public void addRevokeUserId(String userId) {
revokeUserIds.add(userId);
}
public void removeRevokeUserId(String userId) {
revokeUserIds.remove(userId);
}
public void addOrReplaceSubkeyChange(SubkeyChange newChange) {
SubkeyChange foundSubkeyChange = getSubkeyChange(newChange.getSubKeyId());
if (foundSubkeyChange != null) {
changeSubKeys.remove(foundSubkeyChange);
}
changeSubKeys.add(newChange);
}
public void removeSubkeyChange(SubkeyChange change) {
changeSubKeys.remove(change);
}
public SubkeyChange getSubkeyChange(long keyId) {
if (changeSubKeys == null) {
return null;
}
for (SubkeyChange subkeyChange : changeSubKeys) {
if (subkeyChange.getSubKeyId() == keyId) {
return subkeyChange;
}
}
return null;
}
public void addUserAttribute(WrappedUserAttribute ua) {
addUserAttribute.add(ua);
}
abstract SaveKeyringParcel autoBuild();
public SaveKeyringParcel build() {
setAddUserAttribute(Collections.unmodifiableList(addUserAttribute));
setRevokeSubKeys(Collections.unmodifiableList(revokeSubKeys));
setRevokeUserIds(Collections.unmodifiableList(revokeUserIds));
setAddSubKeys(Collections.unmodifiableList(addSubKeys));
setAddUserIds(Collections.unmodifiableList(addUserIds));
setChangeSubKeys(Collections.unmodifiableList(changeSubKeys));
return autoBuild();
}
public boolean hasModificationsForSubkey(long keyId) {
return revokeSubKeys.contains(keyId) || getSubkeyChange(keyId) != null;
}
public void removeModificationsForSubkey(long keyId) {
revokeSubKeys.remove(keyId);
SubkeyChange subkeyChange = getSubkeyChange(keyId);
if (subkeyChange != null) {
changeSubKeys.remove(subkeyChange);
}
}
}
// performance gain for using Parcelable here would probably be negligible,
// use Serializable instead.
@AutoValue
public abstract static class SubkeyAdd implements Serializable {
public abstract Algorithm getAlgorithm();
@Nullable
public abstract Integer getKeySize();
@Nullable
public abstract Curve getCurve();
public abstract int getFlags();
@Nullable
public abstract Long getExpiry();
public static SubkeyAdd createSubkeyAdd(Algorithm algorithm, Integer keySize, Curve curve, int flags,
Long expiry) {
return new AutoValue_SaveKeyringParcel_SubkeyAdd(algorithm, keySize, curve, flags, expiry);
}
public boolean canCertify() {
return (getFlags() & KeyFlags.CERTIFY_OTHER) > 0;
}
public boolean canSign() {
return (getFlags() & KeyFlags.SIGN_DATA) > 0;
}
public boolean canEncrypt() {
return ((getFlags() & KeyFlags.ENCRYPT_COMMS) > 0) || ((getFlags() & KeyFlags.ENCRYPT_STORAGE) > 0);
}
public boolean canAuthenticate() {
return (getFlags() & KeyFlags.AUTHENTICATION) > 0;
}
}
@AutoValue
public abstract static class SubkeyChange implements Serializable {
public abstract long getSubKeyId();
@Nullable
public abstract Integer getFlags();
// this is a long unix timestamp, in seconds (NOT MILLISECONDS!)
@Nullable
public abstract Long getExpiry();
// if this flag is true, the key will be recertified even if all above
// values are no-ops
public abstract boolean getRecertify();
// if this flag is true, the subkey should be changed to a stripped key
public abstract boolean getDummyStrip();
// if this flag is true, the subkey should be moved to a security token
public abstract boolean getMoveKeyToSecurityToken();
// if this is non-null, the subkey will be changed to a divert-to-card
// (security token) key for the given serial number
@Nullable
@SuppressWarnings("mutable")
public abstract byte[] getSecurityTokenSerialNo();
public static SubkeyChange createRecertifyChange(long keyId, boolean recertify) {
return new AutoValue_SaveKeyringParcel_SubkeyChange(keyId, null, null, recertify, false, false, null);
}
public static SubkeyChange createFlagsOrExpiryChange(long keyId, Integer flags, Long expiry) {
return new AutoValue_SaveKeyringParcel_SubkeyChange(keyId, flags, expiry, false, false, false, null);
}
public static SubkeyChange createStripChange(long keyId) {
return new AutoValue_SaveKeyringParcel_SubkeyChange(keyId, null, null, false, true, false, null);
}
public static SubkeyChange createMoveToSecurityTokenChange(long keyId) {
return new AutoValue_SaveKeyringParcel_SubkeyChange(keyId, null, null, false, false, true, null);
}
public static SubkeyChange createSecurityTokenSerialNo(long keyId, byte[] securityTokenSerialNo) {
return new AutoValue_SaveKeyringParcel_SubkeyChange(keyId, null, null, false, false, false, securityTokenSerialNo);
}
}
// All supported algorithms
public enum Algorithm {
RSA, DSA, ELGAMAL, ECDSA, ECDH, EDDSA
}
// All curves defined in the standard
// http://www.bouncycastle.org/wiki/pages/viewpage.action?pageId=362269
public enum Curve {
NIST_P256, NIST_P384, NIST_P521,
// these are supported by gpg, but they are not in rfc6637 and not supported by BouncyCastle yet
// (adding support would be trivial though -> JcaPGPKeyConverter.java:190)
// BRAINPOOL_P256, BRAINPOOL_P384, BRAINPOOL_P512
}
}