From 778fb8e94acd1483c163aefc02517f8676301798 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 30 Oct 2017 21:45:38 +0100 Subject: [PATCH] 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. --- .../keychain/securitytoken/KeyType.java | 14 +++-- .../SecurityTokenConnection.java | 52 +++++++------------ .../securitytoken/SecurityTokenUtils.java | 9 ++-- 3 files changed, 34 insertions(+), 41 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/KeyType.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/KeyType.java index 59c0dc89e..da27a3ccb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/KeyType.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/KeyType.java @@ -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; + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java index 70fff4866..644c992ec 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenConnection.java @@ -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: diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenUtils.java index 31df10576..35f12a317 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenUtils.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenUtils.java @@ -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()) {