Retain RSA key format when setting key attributes in putKey operation

For the put secret key operation, openpgp applet implementations differ
in their handling of attributes:

- there are four formats for sending key data: standard, standard with
  modulus, with crt, and with crt and modulus.
- the key attributes (modulus length, public exponent length, key
  format) can not be changed on all cards. changing them is only
  necessary for cards that support different key lengths (that is,
  RSA 4096)
- on the cards where they *can* be changed, not all parameters might be
  changeable. in particular, modulus length may be changeable but not
  key format.

Because of this constellation, the put key operation now only sets the
modulus of the key, while retaining the key format. At the time of
writing, the Gnuk and Nitrokey use the standard format, while the
Yubikey and other applets use crt+modulus.

This fixes loading keys into the Nitrokey Pro, and partially for the
Gnuk token.
This commit is contained in:
Vincent Breitmoser 2017-10-30 21:45:38 +01:00
parent 5f622339b1
commit 778fb8e94a
3 changed files with 34 additions and 41 deletions

View file

@ -20,20 +20,22 @@ package org.sufficientlysecure.keychain.securitytoken;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
public enum KeyType {
SIGN(0, 0xB6, 0xCE, 0xC7),
ENCRYPT(1, 0xB8, 0xCF, 0xC8),
AUTH(2, 0xA4, 0xD0, 0xC9),;
SIGN(0, 0xB6, 0xCE, 0xC7, 0xC1),
ENCRYPT(1, 0xB8, 0xCF, 0xC8, 0xC2),
AUTH(2, 0xA4, 0xD0, 0xC9, 0xC3);
private final int mIdx;
private final int mSlot;
private final int mTimestampObjectId;
private final int mFingerprintObjectId;
private final int mAlgoAttributeSlot;
KeyType(final int idx, final int slot, final int timestampObjectId, final int fingerprintObjectId) {
KeyType(int idx, int slot, int timestampObjectId, int fingerprintObjectId, int algoAttributeSlot) {
this.mIdx = idx;
this.mSlot = slot;
this.mTimestampObjectId = timestampObjectId;
this.mFingerprintObjectId = fingerprintObjectId;
this.mAlgoAttributeSlot = algoAttributeSlot;
}
public static KeyType from(final CanonicalizedSecretKey key) {
@ -62,4 +64,8 @@ public enum KeyType {
public int getFingerprintObjectId() {
return mFingerprintObjectId;
}
public int getAlgoAttributeSlot() {
return mAlgoAttributeSlot;
}
}

View file

@ -193,8 +193,7 @@ public class SecurityTokenConnection {
throw new CardException("Initialization failed!", response.getSw());
}
OpenPgpCapabilities openPgpCapabilities = new OpenPgpCapabilities(getData(0x00, 0x6E));
setConnectionCapabilities(openPgpCapabilities);
refreshConnectionCapabilities();
mPw1ValidatedForSignature = false;
mPw1ValidatedForDecrypt = false;
@ -235,6 +234,13 @@ public class SecurityTokenConnection {
tokenType = TokenType.UNKNOWN;
}
private void refreshConnectionCapabilities() throws IOException {
byte[] rawOpenPgpCapabilities = getData(0x00, 0x6E);
OpenPgpCapabilities openPgpCapabilities = new OpenPgpCapabilities(rawOpenPgpCapabilities);
setConnectionCapabilities(openPgpCapabilities);
}
@VisibleForTesting
void setConnectionCapabilities(OpenPgpCapabilities openPgpCapabilities) throws IOException {
this.mOpenPgpCapabilities = openPgpCapabilities;
@ -493,33 +499,13 @@ public class SecurityTokenConnection {
}
}
private void setKeyAttributes(Passphrase adminPin, final KeyType slot, final CanonicalizedSecretKey secretKey)
throws IOException {
if (mOpenPgpCapabilities.isAttributesChangable()) {
int tag;
if (slot == KeyType.SIGN) {
tag = 0xC1;
} else if (slot == KeyType.ENCRYPT) {
tag = 0xC2;
} else if (slot == KeyType.AUTH) {
tag = 0xC3;
} else {
throw new IOException("Unknown key for card.");
}
try {
putData(adminPin, tag, SecurityTokenUtils.attributesFromSecretKey(slot, secretKey));
mOpenPgpCapabilities.updateWithData(getData(0x00, tag));
} catch (PgpGeneralException e) {
throw new IOException("Key algorithm not supported by the security token.");
}
private void setKeyAttributes(Passphrase adminPin, KeyType keyType, byte[] data) throws IOException {
if (!mOpenPgpCapabilities.isAttributesChangable()) {
return;
}
putData(adminPin, keyType.getAlgoAttributeSlot(), data);
refreshConnectionCapabilities();
}
/**
@ -546,9 +532,11 @@ public class SecurityTokenConnection {
try {
secretKey.unlock(passphrase);
setKeyAttributes(adminPin, slot, secretKey);
setKeyAttributes(adminPin, slot, SecurityTokenUtils.attributesFromSecretKey(slot, secretKey,
mOpenPgpCapabilities.getFormatForKeyType(slot)));
switch (mOpenPgpCapabilities.getFormatForKeyType(slot).keyFormatType()) {
KeyFormat formatForKeyType = mOpenPgpCapabilities.getFormatForKeyType(slot);
switch (formatForKeyType.keyFormatType()) {
case RSAKeyFormatType:
if (!secretKey.isRSA()) {
throw new IOException("Security Token not configured for RSA key.");
@ -561,7 +549,7 @@ public class SecurityTokenConnection {
}
keyBytes = SecurityTokenUtils.createRSAPrivKeyTemplate(crtSecretKey, slot,
(RSAKeyFormat) (mOpenPgpCapabilities.getFormatForKeyType(slot)));
(RSAKeyFormat) formatForKeyType);
break;
case ECKeyFormatType:
@ -574,7 +562,7 @@ public class SecurityTokenConnection {
ecPublicKey = secretKey.getSecurityTokenECPublicKey();
keyBytes = SecurityTokenUtils.createECPrivKeyTemplate(ecSecretKey, ecPublicKey, slot,
(ECKeyFormat) (mOpenPgpCapabilities.getFormatForKeyType(slot)));
(ECKeyFormat) formatForKeyType);
break;
default:

View file

@ -22,9 +22,6 @@ import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.securitytoken.ECKeyFormat;
import org.sufficientlysecure.keychain.securitytoken.RSAKeyFormat;
import org.sufficientlysecure.keychain.securitytoken.KeyType;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@ -33,8 +30,10 @@ import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateCrtKey;
class SecurityTokenUtils {
static byte[] attributesFromSecretKey(final KeyType slot, final CanonicalizedSecretKey secretKey) throws IOException, PgpGeneralException {
static byte[] attributesFromSecretKey(KeyType slot, CanonicalizedSecretKey secretKey, KeyFormat formatForKeyType)
throws IOException, PgpGeneralException {
if (secretKey.isRSA()) {
final int mModulusLength = secretKey.getBitStrength();
final int mExponentLength = secretKey.getSecurityTokenRSASecretKey().getPublicExponent().bitLength();
@ -46,7 +45,7 @@ class SecurityTokenUtils {
attrs[i++] = (byte) (mModulusLength & 0xff);
attrs[i++] = (byte) ((mExponentLength >> 8) & 0xff);
attrs[i++] = (byte) (mExponentLength & 0xff);
attrs[i] = RSAKeyFormat.RSAAlgorithmFormat.CRT_WITH_MODULUS.getValue();
attrs[i] = ((RSAKeyFormat) formatForKeyType).getAlgorithmFormat().getValue();
return attrs;
} else if (secretKey.isEC()) {