better handling of divert-to-card keys for subkey modifications
This commit is contained in:
@@ -499,6 +499,7 @@ public abstract class OperationResult implements Parcelable {
|
|||||||
MSG_MF_ERROR_RESTRICTED(LogLevel.ERROR, R.string.msg_mf_error_restricted),
|
MSG_MF_ERROR_RESTRICTED(LogLevel.ERROR, R.string.msg_mf_error_restricted),
|
||||||
MSG_MF_ERROR_REVOKED_PRIMARY (LogLevel.ERROR, R.string.msg_mf_error_revoked_primary),
|
MSG_MF_ERROR_REVOKED_PRIMARY (LogLevel.ERROR, R.string.msg_mf_error_revoked_primary),
|
||||||
MSG_MF_ERROR_SIG (LogLevel.ERROR, R.string.msg_mf_error_sig),
|
MSG_MF_ERROR_SIG (LogLevel.ERROR, R.string.msg_mf_error_sig),
|
||||||
|
MSG_MF_ERROR_SUB_STRIPPED(LogLevel.ERROR, R.string.msg_mf_error_sub_stripped),
|
||||||
MSG_MF_ERROR_SUBKEY_MISSING(LogLevel.ERROR, R.string.msg_mf_error_subkey_missing),
|
MSG_MF_ERROR_SUBKEY_MISSING(LogLevel.ERROR, R.string.msg_mf_error_subkey_missing),
|
||||||
MSG_MF_ERROR_CONFLICTING_NFC_COMMANDS(LogLevel.ERROR, R.string.msg_mf_error_conflicting_nfc_commands),
|
MSG_MF_ERROR_CONFLICTING_NFC_COMMANDS(LogLevel.ERROR, R.string.msg_mf_error_conflicting_nfc_commands),
|
||||||
MSG_MF_ERROR_DUPLICATE_KEYTOCARD_FOR_SLOT(LogLevel.ERROR, R.string.msg_mf_error_duplicate_keytocard_for_slot),
|
MSG_MF_ERROR_DUPLICATE_KEYTOCARD_FOR_SLOT(LogLevel.ERROR, R.string.msg_mf_error_duplicate_keytocard_for_slot),
|
||||||
|
|||||||
@@ -897,18 +897,35 @@ public class PgpKeyOperation {
|
|||||||
pKey = PGPPublicKey.removeCertification(pKey, sig);
|
pKey = PGPPublicKey.removeCertification(pKey, sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
|
PGPPrivateKey subPrivateKey;
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
if (!isDivertToCard(sKey)) {
|
||||||
cryptoInput.getPassphrase().getCharArray());
|
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
|
||||||
PGPPrivateKey subPrivateKey = sKey.extractPrivateKey(keyDecryptor);
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
||||||
PGPSignature sig = generateSubkeyBindingSignature(
|
cryptoInput.getPassphrase().getCharArray());
|
||||||
getSignatureGenerator(masterSecretKey, cryptoInput),
|
subPrivateKey = sKey.extractPrivateKey(keyDecryptor);
|
||||||
cryptoInput.getSignatureTime(),
|
// super special case: subkey is allowed to sign, but isn't available
|
||||||
masterPublicKey, masterPrivateKey, subPrivateKey, pKey, flags, expiry);
|
if (subPrivateKey == null) {
|
||||||
|
log.add(LogType.MSG_MF_ERROR_SUB_STRIPPED,
|
||||||
|
indent + 1, KeyFormattingUtils.convertKeyIdToHex(change.mKeyId));
|
||||||
|
return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
subPrivateKey = null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
PGPSignature sig = generateSubkeyBindingSignature(
|
||||||
|
getSignatureGenerator(masterSecretKey, cryptoInput),
|
||||||
|
cryptoInput.getSignatureTime(), masterPublicKey, masterPrivateKey,
|
||||||
|
getSignatureGenerator(sKey, cryptoInput), subPrivateKey,
|
||||||
|
pKey, flags, expiry);
|
||||||
|
|
||||||
|
// generate and add new signature
|
||||||
|
pKey = PGPPublicKey.addCertification(pKey, sig);
|
||||||
|
sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
|
||||||
|
} catch (NfcInteractionNeeded e) {
|
||||||
|
nfcSignOps.addHash(e.hashToSign, e.hashAlgo);
|
||||||
|
}
|
||||||
|
|
||||||
// generate and add new signature
|
|
||||||
pKey = PGPPublicKey.addCertification(pKey, sig);
|
|
||||||
sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
|
|
||||||
}
|
}
|
||||||
subProgressPop();
|
subProgressPop();
|
||||||
|
|
||||||
@@ -987,7 +1004,8 @@ public class PgpKeyOperation {
|
|||||||
PGPSignature cert = generateSubkeyBindingSignature(
|
PGPSignature cert = generateSubkeyBindingSignature(
|
||||||
getSignatureGenerator(masterSecretKey, cryptoInput),
|
getSignatureGenerator(masterSecretKey, cryptoInput),
|
||||||
cryptoInput.getSignatureTime(),
|
cryptoInput.getSignatureTime(),
|
||||||
masterPublicKey, masterPrivateKey, keyPair.getPrivateKey(), pKey,
|
masterPublicKey, masterPrivateKey,
|
||||||
|
getSignatureGenerator(pKey, cryptoInput, false), keyPair.getPrivateKey(), pKey,
|
||||||
add.mFlags, add.mExpiry);
|
add.mFlags, add.mExpiry);
|
||||||
pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert);
|
pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert);
|
||||||
} catch (NfcInteractionNeeded e) {
|
} catch (NfcInteractionNeeded e) {
|
||||||
@@ -1002,7 +1020,8 @@ public class PgpKeyOperation {
|
|||||||
PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
|
PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
|
||||||
PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
|
PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
||||||
cryptoInput.getPassphrase().getCharArray());
|
cryptoInput.hasPassphrase()
|
||||||
|
? cryptoInput.getPassphrase().getCharArray() : new char[]{} );
|
||||||
|
|
||||||
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
|
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
|
||||||
.build().get(PgpConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO);
|
.build().get(PgpConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO);
|
||||||
@@ -1402,22 +1421,27 @@ public class PgpKeyOperation {
|
|||||||
static PGPSignatureGenerator getSignatureGenerator(
|
static PGPSignatureGenerator getSignatureGenerator(
|
||||||
PGPSecretKey secretKey, CryptoInputParcel cryptoInput) {
|
PGPSecretKey secretKey, CryptoInputParcel cryptoInput) {
|
||||||
|
|
||||||
PGPContentSignerBuilder builder;
|
|
||||||
|
|
||||||
S2K s2k = secretKey.getS2K();
|
S2K s2k = secretKey.getS2K();
|
||||||
if (s2k != null && s2k.getType() == S2K.GNU_DUMMY_S2K
|
boolean isDivertToCard = s2k != null && s2k.getType() == S2K.GNU_DUMMY_S2K
|
||||||
&& s2k.getProtectionMode() == S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD) {
|
&& s2k.getProtectionMode() == S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD;
|
||||||
|
|
||||||
|
return getSignatureGenerator(secretKey.getPublicKey(), cryptoInput, isDivertToCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PGPSignatureGenerator getSignatureGenerator(
|
||||||
|
PGPPublicKey pKey, CryptoInputParcel cryptoInput, boolean divertToCard) {
|
||||||
|
|
||||||
|
PGPContentSignerBuilder builder;
|
||||||
|
if (divertToCard) {
|
||||||
// use synchronous "NFC based" SignerBuilder
|
// use synchronous "NFC based" SignerBuilder
|
||||||
builder = new NfcSyncPGPContentSignerBuilder(
|
builder = new NfcSyncPGPContentSignerBuilder(
|
||||||
secretKey.getPublicKey().getAlgorithm(),
|
pKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO,
|
||||||
PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO,
|
pKey.getKeyID(), cryptoInput.getCryptoData())
|
||||||
secretKey.getKeyID(), cryptoInput.getCryptoData())
|
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
} else {
|
} else {
|
||||||
// content signer based on signing key algorithm and chosen hash algorithm
|
// content signer based on signing key algorithm and chosen hash algorithm
|
||||||
builder = new JcaPGPContentSignerBuilder(
|
builder = new JcaPGPContentSignerBuilder(
|
||||||
secretKey.getPublicKey().getAlgorithm(),
|
pKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
||||||
PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1524,7 +1548,8 @@ public class PgpKeyOperation {
|
|||||||
static PGPSignature generateSubkeyBindingSignature(
|
static PGPSignature generateSubkeyBindingSignature(
|
||||||
PGPSignatureGenerator sGen, Date creationTime,
|
PGPSignatureGenerator sGen, Date creationTime,
|
||||||
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
|
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
|
||||||
PGPPrivateKey subPrivateKey, PGPPublicKey pKey, int flags, long expiry)
|
PGPSignatureGenerator subSigGen, PGPPrivateKey subPrivateKey, PGPPublicKey pKey,
|
||||||
|
int flags, long expiry)
|
||||||
throws IOException, PGPException, SignatureException {
|
throws IOException, PGPException, SignatureException {
|
||||||
|
|
||||||
PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||||
@@ -1534,10 +1559,6 @@ public class PgpKeyOperation {
|
|||||||
// cross-certify signing keys
|
// cross-certify signing keys
|
||||||
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||||
subHashedPacketsGen.setSignatureCreationTime(false, creationTime);
|
subHashedPacketsGen.setSignatureCreationTime(false, creationTime);
|
||||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
|
||||||
pKey.getAlgorithm(), PgpConstants.SECRET_KEY_SIGNATURE_HASH_ALGO)
|
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
|
||||||
PGPSignatureGenerator subSigGen = new PGPSignatureGenerator(signerBuilder);
|
|
||||||
subSigGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey);
|
subSigGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey);
|
||||||
subSigGen.setHashedSubpackets(subHashedPacketsGen.generate());
|
subSigGen.setHashedSubpackets(subHashedPacketsGen.generate());
|
||||||
PGPSignature certification = subSigGen.generateCertification(masterPublicKey, pKey);
|
PGPSignature certification = subSigGen.generateCertification(masterPublicKey, pKey);
|
||||||
|
|||||||
@@ -991,6 +991,7 @@
|
|||||||
<string name="msg_mf_error_passphrase_master">"Fatal error decrypting master key! This is likely a programming error, please file a bug report!"</string>
|
<string name="msg_mf_error_passphrase_master">"Fatal error decrypting master key! This is likely a programming error, please file a bug report!"</string>
|
||||||
<string name="msg_mf_error_pgp">"Internal OpenPGP error!"</string>
|
<string name="msg_mf_error_pgp">"Internal OpenPGP error!"</string>
|
||||||
<string name="msg_mf_error_sig">"Signature exception!"</string>
|
<string name="msg_mf_error_sig">"Signature exception!"</string>
|
||||||
|
<string name="msg_mf_error_sub_stripped">"Cannot modify stripped subkey %s!"</string>
|
||||||
<string name="msg_mf_error_subkey_missing">"Tried to operate on missing subkey %s!"</string>
|
<string name="msg_mf_error_subkey_missing">"Tried to operate on missing subkey %s!"</string>
|
||||||
<string name="msg_mf_error_conflicting_nfc_commands">"Cannot move key to smart card in same operation that creates an on-card signature."</string>
|
<string name="msg_mf_error_conflicting_nfc_commands">"Cannot move key to smart card in same operation that creates an on-card signature."</string>
|
||||||
<string name="msg_mf_error_duplicate_keytocard_for_slot">"Smart card supports only one slot per key type."</string>
|
<string name="msg_mf_error_duplicate_keytocard_for_slot">"Smart card supports only one slot per key type."</string>
|
||||||
|
|||||||
@@ -94,11 +94,11 @@ public class PgpKeyOperationTest {
|
|||||||
|
|
||||||
SaveKeyringParcel parcel = new SaveKeyringParcel();
|
SaveKeyringParcel parcel = new SaveKeyringParcel();
|
||||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||||
Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
|
Algorithm.DSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
|
||||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||||
Algorithm.DSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
|
Algorithm.RSA, 2048, null, KeyFlags.SIGN_DATA, 0L));
|
||||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||||
Algorithm.RSA, 2048, null, KeyFlags.ENCRYPT_COMMS, 0L));
|
Algorithm.RSA, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L));
|
||||||
|
|
||||||
parcel.mAddUserIds.add("twi");
|
parcel.mAddUserIds.add("twi");
|
||||||
parcel.mAddUserIds.add("pink");
|
parcel.mAddUserIds.add("pink");
|
||||||
@@ -821,6 +821,15 @@ public class PgpKeyOperationTest {
|
|||||||
Assert.assertEquals("new packet should have GNU_DUMMY protection mode stripped",
|
Assert.assertEquals("new packet should have GNU_DUMMY protection mode stripped",
|
||||||
S2K.GNU_PROTECTION_MODE_NO_PRIVATE_KEY, ((SecretKeyPacket) p).getS2K().getProtectionMode());
|
S2K.GNU_PROTECTION_MODE_NO_PRIVATE_KEY, ((SecretKeyPacket) p).getS2K().getProtectionMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{ // trying to edit a subkey with signing capability should fail
|
||||||
|
parcel.reset();
|
||||||
|
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true));
|
||||||
|
|
||||||
|
assertModifyFailure("subkey modification for signing-enabled but stripped subkey should fail",
|
||||||
|
modified, parcel, LogType.MSG_MF_ERROR_SUB_STRIPPED);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -829,7 +838,7 @@ public class PgpKeyOperationTest {
|
|||||||
UncachedKeyRing modified;
|
UncachedKeyRing modified;
|
||||||
|
|
||||||
{ // keytocard should fail with BAD_NFC_SIZE when presented with the RSA-1024 key
|
{ // keytocard should fail with BAD_NFC_SIZE when presented with the RSA-1024 key
|
||||||
long keyId = KeyringTestingHelper.getSubkeyId(ring, 0);
|
long keyId = KeyringTestingHelper.getSubkeyId(ring, 2);
|
||||||
parcel.reset();
|
parcel.reset();
|
||||||
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, false, true));
|
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, false, true));
|
||||||
|
|
||||||
@@ -838,7 +847,7 @@ public class PgpKeyOperationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{ // keytocard should fail with BAD_NFC_ALGO when presented with the DSA-1024 key
|
{ // keytocard should fail with BAD_NFC_ALGO when presented with the DSA-1024 key
|
||||||
long keyId = KeyringTestingHelper.getSubkeyId(ring, 1);
|
long keyId = KeyringTestingHelper.getSubkeyId(ring, 0);
|
||||||
parcel.reset();
|
parcel.reset();
|
||||||
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, false, true));
|
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, false, true));
|
||||||
|
|
||||||
@@ -846,9 +855,10 @@ public class PgpKeyOperationTest {
|
|||||||
parcel, cryptoInput, LogType.MSG_MF_ERROR_BAD_NFC_ALGO);
|
parcel, cryptoInput, LogType.MSG_MF_ERROR_BAD_NFC_ALGO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long keyId = KeyringTestingHelper.getSubkeyId(ring, 1);
|
||||||
|
|
||||||
{ // keytocard should return a pending NFC_MOVE_KEY_TO_CARD result when presented with the RSA-2048
|
{ // keytocard should return a pending NFC_MOVE_KEY_TO_CARD result when presented with the RSA-2048
|
||||||
// key, and then make key divert-to-card when it gets a serial in the cryptoInputParcel.
|
// key, and then make key divert-to-card when it gets a serial in the cryptoInputParcel.
|
||||||
long keyId = KeyringTestingHelper.getSubkeyId(ring, 2);
|
|
||||||
parcel.reset();
|
parcel.reset();
|
||||||
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, false, true));
|
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, false, true));
|
||||||
|
|
||||||
@@ -880,7 +890,19 @@ public class PgpKeyOperationTest {
|
|||||||
S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD, ((SecretKeyPacket) p).getS2K().getProtectionMode());
|
S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD, ((SecretKeyPacket) p).getS2K().getProtectionMode());
|
||||||
Assert.assertArrayEquals("new packet should have correct serial number as iv",
|
Assert.assertArrayEquals("new packet should have correct serial number as iv",
|
||||||
serial, ((SecretKeyPacket) p).getIV());
|
serial, ((SecretKeyPacket) p).getIV());
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // editing a signing subkey requires a primary key binding sig -> pendinginput
|
||||||
|
parcel.reset();
|
||||||
|
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true));
|
||||||
|
|
||||||
|
CanonicalizedSecretKeyRing secretRing =
|
||||||
|
new CanonicalizedSecretKeyRing(modified.getEncoded(), false, 0);
|
||||||
|
PgpKeyOperation op = new PgpKeyOperation(null);
|
||||||
|
PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, cryptoInput, parcel);
|
||||||
|
Assert.assertTrue("keytocard operation should be pending", result.isPending());
|
||||||
|
Assert.assertEquals("required input should be RequiredInputType.NFC_SIGN",
|
||||||
|
RequiredInputType.NFC_SIGN, result.getRequiredInputParcel().mType);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -558,8 +558,9 @@ public class UncachedKeyringCanonicalizeTest {
|
|||||||
PGPSignature cert = PgpKeyOperation.generateSubkeyBindingSignature(
|
PGPSignature cert = PgpKeyOperation.generateSubkeyBindingSignature(
|
||||||
PgpKeyOperation.getSignatureGenerator(masterSecretKey.getSecretKey(), cryptoInput),
|
PgpKeyOperation.getSignatureGenerator(masterSecretKey.getSecretKey(), cryptoInput),
|
||||||
cryptoInput.getSignatureTime(),
|
cryptoInput.getSignatureTime(),
|
||||||
masterPublicKey, masterSecretKey.getPrivateKey(), masterSecretKey.getPrivateKey(),
|
masterPublicKey, masterSecretKey.getPrivateKey(),
|
||||||
masterPublicKey, masterSecretKey.getKeyUsage(), 0);
|
PgpKeyOperation.getSignatureGenerator(masterSecretKey.getSecretKey(), null),
|
||||||
|
masterSecretKey.getPrivateKey(), masterPublicKey, masterSecretKey.getKeyUsage(), 0);
|
||||||
PGPPublicKey subPubKey = PGPPublicKey.addSubkeyBindingCertification(masterPublicKey, cert);
|
PGPPublicKey subPubKey = PGPPublicKey.addSubkeyBindingCertification(masterPublicKey, cert);
|
||||||
|
|
||||||
PGPSecretKey sKey;
|
PGPSecretKey sKey;
|
||||||
|
|||||||
Reference in New Issue
Block a user