Merge pull request #2217 from hagau/cleanup_auth_sig_gen
Fix EdDSA signature generation for authentication & clean up authentication signature generator
This commit is contained in:
@@ -26,9 +26,10 @@ import java.security.interfaces.RSAPrivateCrtKey;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
|
||||||
import org.bouncycastle.bcpg.S2K;
|
import org.bouncycastle.bcpg.S2K;
|
||||||
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
|
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
|
||||||
import org.bouncycastle.openpgp.PGPAuthenticationSignatureGenerator;
|
import org.bouncycastle.openpgp.AuthenticationSignatureGenerator;
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPPrivateKey;
|
import org.bouncycastle.openpgp.PGPPrivateKey;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKey;
|
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||||
@@ -37,13 +38,7 @@ import org.bouncycastle.openpgp.PGPSignatureGenerator;
|
|||||||
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
|
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
|
||||||
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
|
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
|
||||||
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
|
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
|
||||||
import org.bouncycastle.openpgp.operator.jcajce.CachingDataDecryptorFactory;
|
import org.bouncycastle.openpgp.operator.jcajce.*;
|
||||||
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
|
|
||||||
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyConverter;
|
|
||||||
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
|
|
||||||
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
|
|
||||||
import org.bouncycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder;
|
|
||||||
import org.bouncycastle.openpgp.operator.jcajce.SessionKeySecretKeyDecryptorBuilder;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||||
@@ -219,8 +214,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PGPContentSignerBuilder getContentSignerBuilder(int hashAlgo,
|
private PGPContentSignerBuilder getContentSignerBuilder(int hashAlgo, Map<ByteBuffer, byte[]> signedHashes) {
|
||||||
Map<ByteBuffer,byte[]> signedHashes) {
|
|
||||||
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
|
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
|
||||||
// use synchronous "NFC based" SignerBuilder
|
// use synchronous "NFC based" SignerBuilder
|
||||||
return new NfcSyncPGPContentSignerBuilder(
|
return new NfcSyncPGPContentSignerBuilder(
|
||||||
@@ -253,17 +247,33 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public PGPAuthenticationSignatureGenerator getAuthenticationSignatureGenerator(int hashAlgorithm,
|
|
||||||
Map<ByteBuffer, byte[]> signedHashes)
|
private PGPContentSignerBuilder getAuthenticationContentSignerBuilder(int hashAlgorithm, Map<ByteBuffer,
|
||||||
|
byte[]> signedHashes) {
|
||||||
|
if (getAlgorithm() == PublicKeyAlgorithmTags.EDDSA) {
|
||||||
|
// content signer feeding the input directly into the signature engine,
|
||||||
|
// since EdDSA hashes the input anyway
|
||||||
|
return new EdDsaAuthenticationContentSignerBuilder(
|
||||||
|
mSecretKey.getPublicKey().getAlgorithm(), hashAlgorithm)
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
|
} else {
|
||||||
|
return getContentSignerBuilder(hashAlgorithm, signedHashes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticationSignatureGenerator getAuthenticationSignatureGenerator(int hashAlgorithm,
|
||||||
|
Map<ByteBuffer, byte[]> signedHashes)
|
||||||
throws PgpGeneralException {
|
throws PgpGeneralException {
|
||||||
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
|
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
|
||||||
throw new PrivateKeyNotUnlockedException();
|
throw new PrivateKeyNotUnlockedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(hashAlgorithm, signedHashes);
|
PGPContentSignerBuilder contentSignerBuilder =
|
||||||
|
getAuthenticationContentSignerBuilder(hashAlgorithm, signedHashes);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
PGPAuthenticationSignatureGenerator signatureGenerator = new PGPAuthenticationSignatureGenerator(contentSignerBuilder);
|
AuthenticationSignatureGenerator signatureGenerator =
|
||||||
|
new AuthenticationSignatureGenerator(contentSignerBuilder);
|
||||||
signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, mPrivateKey);
|
signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, mPrivateKey);
|
||||||
|
|
||||||
return signatureGenerator;
|
return signatureGenerator;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package org.sufficientlysecure.keychain.ssh;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import org.bouncycastle.openpgp.PGPAuthenticationSignatureGenerator;
|
import org.bouncycastle.openpgp.AuthenticationSignatureGenerator;
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder;
|
import org.bouncycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder;
|
||||||
import org.sufficientlysecure.keychain.operations.BaseOperation;
|
import org.sufficientlysecure.keychain.operations.BaseOperation;
|
||||||
@@ -206,7 +206,7 @@ public class AuthenticationOperation extends BaseOperation<AuthenticationParcel>
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PGPAuthenticationSignatureGenerator signatureGenerator;
|
AuthenticationSignatureGenerator signatureGenerator;
|
||||||
try {
|
try {
|
||||||
signatureGenerator = authKey.getAuthenticationSignatureGenerator(
|
signatureGenerator = authKey.getAuthenticationSignatureGenerator(
|
||||||
hashAlgorithm, cryptoInput.getCryptoData());
|
hashAlgorithm, cryptoInput.getCryptoData());
|
||||||
@@ -218,7 +218,7 @@ public class AuthenticationOperation extends BaseOperation<AuthenticationParcel>
|
|||||||
signatureGenerator.update(challenge, 0, challenge.length);
|
signatureGenerator.update(challenge, 0, challenge.length);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
signature = signatureGenerator.generate().getSignature();
|
signature = signatureGenerator.getSignature();
|
||||||
} catch (NfcSyncPGPContentSignerBuilder.NfcInteractionNeeded e) {
|
} catch (NfcSyncPGPContentSignerBuilder.NfcInteractionNeeded e) {
|
||||||
// this secret key diverts to a OpenPGP card, thus requires user interaction
|
// this secret key diverts to a OpenPGP card, thus requires user interaction
|
||||||
log.add(LogType.MSG_AUTH_PENDING_NFC, indent);
|
log.add(LogType.MSG_AUTH_PENDING_NFC, indent);
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ package org.sufficientlysecure.keychain.operations;
|
|||||||
|
|
||||||
|
|
||||||
import org.bouncycastle.bcpg.HashAlgorithmTags;
|
import org.bouncycastle.bcpg.HashAlgorithmTags;
|
||||||
|
import org.bouncycastle.jcajce.provider.asymmetric.eddsa.EdDSAEngine;
|
||||||
|
import org.bouncycastle.jcajce.provider.asymmetric.eddsa.spec.EdDSANamedCurveTable;
|
||||||
|
import org.bouncycastle.jcajce.provider.asymmetric.eddsa.spec.EdDSAParameterSpec;
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -42,6 +45,7 @@ import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
|
|||||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||||
|
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
|
import java.security.MessageDigest;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
import java.security.Signature;
|
import java.security.Signature;
|
||||||
@@ -50,35 +54,84 @@ import java.util.ArrayList;
|
|||||||
@RunWith(KeychainTestRunner.class)
|
@RunWith(KeychainTestRunner.class)
|
||||||
public class AuthenticationOperationTest {
|
public class AuthenticationOperationTest {
|
||||||
|
|
||||||
private static UncachedKeyRing mStaticRing;
|
private static UncachedKeyRing mStaticRingRsa;
|
||||||
|
private static UncachedKeyRing mStaticRingEcDsa;
|
||||||
|
private static UncachedKeyRing mStaticRingEdDsa;
|
||||||
|
private static UncachedKeyRing mStaticRingDsa;
|
||||||
private static Passphrase mKeyPhrase;
|
private static Passphrase mKeyPhrase;
|
||||||
|
|
||||||
private static PrintStream oldShadowStream;
|
private static PrintStream oldShadowStream;
|
||||||
|
|
||||||
|
/*
|
||||||
|
private static void generateKeys() throws IOException {
|
||||||
|
PgpKeyOperation op = new PgpKeyOperation(null);
|
||||||
|
SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildNewKeyringParcel();
|
||||||
|
|
||||||
|
builder.addSubkeyAdd(SaveKeyringParcel.SubkeyAdd.createSubkeyAdd(
|
||||||
|
SaveKeyringParcel.Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
|
||||||
|
builder.addSubkeyAdd(SaveKeyringParcel.SubkeyAdd.createSubkeyAdd(
|
||||||
|
SaveKeyringParcel.Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.AUTHENTICATION, 0L));
|
||||||
|
builder.addUserId("blah");
|
||||||
|
builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(new Passphrase("x")));
|
||||||
|
|
||||||
|
PgpEditKeyResult result = op.createSecretKeyRing(builder.build());
|
||||||
|
new FileOutputStream("/tmp/authenticate_ecdsa.sec").write(result.getRing().getEncoded());
|
||||||
|
|
||||||
|
|
||||||
|
op = new PgpKeyOperation(null);
|
||||||
|
builder = SaveKeyringParcel.buildNewKeyringParcel();
|
||||||
|
|
||||||
|
builder.addSubkeyAdd(SaveKeyringParcel.SubkeyAdd.createSubkeyAdd(
|
||||||
|
SaveKeyringParcel.Algorithm.EDDSA, 0, null, KeyFlags.CERTIFY_OTHER, 0L));
|
||||||
|
builder.addSubkeyAdd(SaveKeyringParcel.SubkeyAdd.createSubkeyAdd(
|
||||||
|
SaveKeyringParcel.Algorithm.EDDSA, 0, null, KeyFlags.AUTHENTICATION, 0L));
|
||||||
|
builder.addUserId("blah");
|
||||||
|
builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(new Passphrase("x")));
|
||||||
|
|
||||||
|
result = op.createSecretKeyRing(builder.build());
|
||||||
|
new FileOutputStream("/tmp/authenticate_eddsa.sec").write(result.getRing().getEncoded());
|
||||||
|
|
||||||
|
|
||||||
|
op = new PgpKeyOperation(null);
|
||||||
|
builder = SaveKeyringParcel.buildNewKeyringParcel();
|
||||||
|
|
||||||
|
builder.addSubkeyAdd(SaveKeyringParcel.SubkeyAdd.createSubkeyAdd(
|
||||||
|
SaveKeyringParcel.Algorithm.RSA, 2048, null, KeyFlags.CERTIFY_OTHER, 0L));
|
||||||
|
builder.addSubkeyAdd(SaveKeyringParcel.SubkeyAdd.createSubkeyAdd(
|
||||||
|
SaveKeyringParcel.Algorithm.RSA, 2048, null, KeyFlags.AUTHENTICATION, 0L));
|
||||||
|
builder.addUserId("blah");
|
||||||
|
builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(new Passphrase("x")));
|
||||||
|
|
||||||
|
result = op.createSecretKeyRing(builder.build());
|
||||||
|
new FileOutputStream("/tmp/authenticate_rsa.sec").write(result.getRing().getEncoded());
|
||||||
|
|
||||||
|
|
||||||
|
op = new PgpKeyOperation(null);
|
||||||
|
builder = SaveKeyringParcel.buildNewKeyringParcel();
|
||||||
|
|
||||||
|
builder.addSubkeyAdd(SaveKeyringParcel.SubkeyAdd.createSubkeyAdd(
|
||||||
|
SaveKeyringParcel.Algorithm.DSA, 2048, null, KeyFlags.CERTIFY_OTHER, 0L));
|
||||||
|
builder.addSubkeyAdd(SaveKeyringParcel.SubkeyAdd.createSubkeyAdd(
|
||||||
|
SaveKeyringParcel.Algorithm.DSA, 2048, null, KeyFlags.AUTHENTICATION, 0L));
|
||||||
|
builder.addUserId("blah");
|
||||||
|
builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(new Passphrase("x")));
|
||||||
|
|
||||||
|
result = op.createSecretKeyRing(builder.build());
|
||||||
|
new FileOutputStream("/tmp/authenticate_dsa.sec").write(result.getRing().getEncoded());
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void setUpOnce() throws Exception {
|
public static void setUpOnce() throws Exception {
|
||||||
Security.insertProviderAt(new BouncyCastleProvider(), 1);
|
Security.insertProviderAt(new BouncyCastleProvider(), 1);
|
||||||
oldShadowStream = ShadowLog.stream;
|
oldShadowStream = ShadowLog.stream;
|
||||||
// ShadowLog.stream = System.out;
|
// ShadowLog.stream = System.out;
|
||||||
|
|
||||||
/* keyring generation:
|
|
||||||
PgpKeyOperation op = new PgpKeyOperation(null);
|
|
||||||
SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildNewKeyringParcel();
|
|
||||||
|
|
||||||
builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
|
|
||||||
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
|
|
||||||
builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
|
|
||||||
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.AUTHENTICATION, 0L));
|
|
||||||
builder.addUserId("blah");
|
|
||||||
builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(new Passphrase("x")));
|
|
||||||
|
|
||||||
PgpEditKeyResult result = op.createSecretKeyRing(builder.build());
|
|
||||||
new FileOutputStream("/tmp/authenticate.sec").write(result.getRing().getEncoded());
|
|
||||||
*/
|
|
||||||
|
|
||||||
mKeyPhrase = new Passphrase("x");
|
mKeyPhrase = new Passphrase("x");
|
||||||
mStaticRing = KeyringTestingHelper.readRingFromResource("/test-keys/authenticate.sec");
|
mStaticRingRsa = KeyringTestingHelper.readRingFromResource("/test-keys/authenticate_rsa.sec");
|
||||||
|
mStaticRingEcDsa = KeyringTestingHelper.readRingFromResource("/test-keys/authenticate_ecdsa.sec");
|
||||||
|
mStaticRingEdDsa = KeyringTestingHelper.readRingFromResource("/test-keys/authenticate_eddsa.sec");
|
||||||
|
mStaticRingDsa = KeyringTestingHelper.readRingFromResource("/test-keys/authenticate_dsa.sec");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -89,21 +142,24 @@ public class AuthenticationOperationTest {
|
|||||||
// don't log verbosely here, we're not here to test imports
|
// don't log verbosely here, we're not here to test imports
|
||||||
ShadowLog.stream = oldShadowStream;
|
ShadowLog.stream = oldShadowStream;
|
||||||
|
|
||||||
databaseInteractor.saveSecretKeyRing(mStaticRing);
|
databaseInteractor.saveSecretKeyRing(mStaticRingRsa);
|
||||||
|
databaseInteractor.saveSecretKeyRing(mStaticRingEcDsa);
|
||||||
|
databaseInteractor.saveSecretKeyRing(mStaticRingEdDsa);
|
||||||
|
databaseInteractor.saveSecretKeyRing(mStaticRingDsa);
|
||||||
|
|
||||||
// ok NOW log verbosely!
|
// ok NOW log verbosely!
|
||||||
ShadowLog.stream = System.out;
|
ShadowLog.stream = System.out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAuthenticate() throws Exception {
|
public void testAuthenticateRsa() throws Exception {
|
||||||
|
|
||||||
byte[] challenge = "dies ist ein challenge ☭".getBytes();
|
byte[] challenge = "dies ist ein challenge ☭".getBytes();
|
||||||
byte[] signature;
|
byte[] signature;
|
||||||
|
|
||||||
KeyRepository keyRepository = KeyRepository.create(RuntimeEnvironment.application);
|
KeyRepository keyRepository = KeyRepository.create(RuntimeEnvironment.application);
|
||||||
|
|
||||||
long masterKeyId = mStaticRing.getMasterKeyId();
|
long masterKeyId = mStaticRingRsa.getMasterKeyId();
|
||||||
Long authSubKeyId = keyRepository.getCachedPublicKeyRing(masterKeyId).getSecretAuthenticationId();
|
Long authSubKeyId = keyRepository.getCachedPublicKeyRing(masterKeyId).getSecretAuthenticationId();
|
||||||
|
|
||||||
{ // sign challenge
|
{ // sign challenge
|
||||||
@@ -115,10 +171,6 @@ public class AuthenticationOperationTest {
|
|||||||
authData.setAuthenticationSubKeyId(authSubKeyId);
|
authData.setAuthenticationSubKeyId(authSubKeyId);
|
||||||
authData.setHashAlgorithm(HashAlgorithmTags.SHA512);
|
authData.setHashAlgorithm(HashAlgorithmTags.SHA512);
|
||||||
|
|
||||||
// ArrayList<Long> allowedKeyIds = new ArrayList<>(1);
|
|
||||||
// allowedKeyIds.add(mStaticRing.getMasterKeyId());
|
|
||||||
// authData.setAllowedAuthenticationKeyIds(allowedKeyIds);
|
|
||||||
|
|
||||||
AuthenticationParcel authenticationParcel = AuthenticationParcel
|
AuthenticationParcel authenticationParcel = AuthenticationParcel
|
||||||
.createAuthenticationParcel(authData.build(), challenge);
|
.createAuthenticationParcel(authData.build(), challenge);
|
||||||
|
|
||||||
@@ -133,7 +185,53 @@ public class AuthenticationOperationTest {
|
|||||||
}
|
}
|
||||||
{ // verify signature
|
{ // verify signature
|
||||||
CanonicalizedPublicKey canonicalizedPublicKey = keyRepository.getCanonicalizedPublicKeyRing(masterKeyId)
|
CanonicalizedPublicKey canonicalizedPublicKey = keyRepository.getCanonicalizedPublicKeyRing(masterKeyId)
|
||||||
.getPublicKey(authSubKeyId);
|
.getPublicKey(authSubKeyId);
|
||||||
|
PublicKey publicKey = canonicalizedPublicKey.getJcaPublicKey();
|
||||||
|
|
||||||
|
Signature signatureVerifier = Signature.getInstance("SHA512withRSA");
|
||||||
|
signatureVerifier.initVerify(publicKey);
|
||||||
|
signatureVerifier.update(challenge);
|
||||||
|
boolean isSignatureValid = signatureVerifier.verify(signature);
|
||||||
|
|
||||||
|
Assert.assertTrue("signature must be valid", isSignatureValid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAuthenticateEcDsa() throws Exception {
|
||||||
|
|
||||||
|
byte[] challenge = "dies ist ein challenge ☭".getBytes();
|
||||||
|
byte[] signature;
|
||||||
|
|
||||||
|
KeyRepository keyRepository = KeyRepository.create(RuntimeEnvironment.application);
|
||||||
|
|
||||||
|
long masterKeyId = mStaticRingEcDsa.getMasterKeyId();
|
||||||
|
Long authSubKeyId = keyRepository.getCachedPublicKeyRing(masterKeyId).getSecretAuthenticationId();
|
||||||
|
|
||||||
|
{ // sign challenge
|
||||||
|
AuthenticationOperation op = new AuthenticationOperation(RuntimeEnvironment.application,
|
||||||
|
keyRepository);
|
||||||
|
|
||||||
|
AuthenticationData.Builder authData = AuthenticationData.builder();
|
||||||
|
authData.setAuthenticationMasterKeyId(masterKeyId);
|
||||||
|
authData.setAuthenticationSubKeyId(authSubKeyId);
|
||||||
|
authData.setHashAlgorithm(HashAlgorithmTags.SHA512);
|
||||||
|
|
||||||
|
AuthenticationParcel authenticationParcel = AuthenticationParcel
|
||||||
|
.createAuthenticationParcel(authData.build(), challenge);
|
||||||
|
|
||||||
|
CryptoInputParcel inputParcel = CryptoInputParcel.createCryptoInputParcel();
|
||||||
|
inputParcel = inputParcel.withPassphrase(mKeyPhrase);
|
||||||
|
|
||||||
|
AuthenticationResult result = op.execute(authData.build(), inputParcel, authenticationParcel);
|
||||||
|
|
||||||
|
Assert.assertTrue("authentication must succeed", result.success());
|
||||||
|
|
||||||
|
signature = result.getSignature();
|
||||||
|
}
|
||||||
|
{ // verify signature
|
||||||
|
CanonicalizedPublicKey canonicalizedPublicKey = keyRepository.getCanonicalizedPublicKeyRing(masterKeyId)
|
||||||
|
.getPublicKey(authSubKeyId);
|
||||||
PublicKey publicKey = canonicalizedPublicKey.getJcaPublicKey();
|
PublicKey publicKey = canonicalizedPublicKey.getJcaPublicKey();
|
||||||
|
|
||||||
Signature signatureVerifier = Signature.getInstance("SHA512withECDSA");
|
Signature signatureVerifier = Signature.getInstance("SHA512withECDSA");
|
||||||
@@ -145,6 +243,100 @@ public class AuthenticationOperationTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAuthenticateEdDsa() throws Exception {
|
||||||
|
|
||||||
|
byte[] challenge = "dies ist ein challenge ☭".getBytes();
|
||||||
|
byte[] signature;
|
||||||
|
|
||||||
|
KeyRepository keyRepository = KeyRepository.create(RuntimeEnvironment.application);
|
||||||
|
|
||||||
|
long masterKeyId = mStaticRingEdDsa.getMasterKeyId();
|
||||||
|
Long authSubKeyId = keyRepository.getCachedPublicKeyRing(masterKeyId).getSecretAuthenticationId();
|
||||||
|
|
||||||
|
{ // sign challenge
|
||||||
|
AuthenticationOperation op = new AuthenticationOperation(RuntimeEnvironment.application,
|
||||||
|
keyRepository);
|
||||||
|
|
||||||
|
AuthenticationData.Builder authData = AuthenticationData.builder();
|
||||||
|
authData.setAuthenticationMasterKeyId(masterKeyId);
|
||||||
|
authData.setAuthenticationSubKeyId(authSubKeyId);
|
||||||
|
authData.setHashAlgorithm(HashAlgorithmTags.SHA512);
|
||||||
|
|
||||||
|
AuthenticationParcel authenticationParcel = AuthenticationParcel
|
||||||
|
.createAuthenticationParcel(authData.build(), challenge);
|
||||||
|
|
||||||
|
CryptoInputParcel inputParcel = CryptoInputParcel.createCryptoInputParcel();
|
||||||
|
inputParcel = inputParcel.withPassphrase(mKeyPhrase);
|
||||||
|
|
||||||
|
AuthenticationResult result = op.execute(authData.build(), inputParcel, authenticationParcel);
|
||||||
|
|
||||||
|
Assert.assertTrue("authentication must succeed", result.success());
|
||||||
|
|
||||||
|
signature = result.getSignature();
|
||||||
|
}
|
||||||
|
{ // verify signature
|
||||||
|
CanonicalizedPublicKey canonicalizedPublicKey = keyRepository.getCanonicalizedPublicKeyRing(masterKeyId)
|
||||||
|
.getPublicKey(authSubKeyId);
|
||||||
|
PublicKey publicKey = canonicalizedPublicKey.getJcaPublicKey();
|
||||||
|
|
||||||
|
EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("Ed25519");
|
||||||
|
Signature signatureVerifier = new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm()));
|
||||||
|
signatureVerifier.setParameter(EdDSAEngine.ONE_SHOT_MODE);
|
||||||
|
signatureVerifier.initVerify(publicKey);
|
||||||
|
signatureVerifier.update(challenge);
|
||||||
|
boolean isSignatureValid = signatureVerifier.verify(signature);
|
||||||
|
|
||||||
|
Assert.assertTrue("signature must be valid", isSignatureValid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAuthenticateDsa() throws Exception {
|
||||||
|
|
||||||
|
byte[] challenge = "dies ist ein challenge ☭".getBytes();
|
||||||
|
byte[] signature;
|
||||||
|
|
||||||
|
KeyRepository keyRepository = KeyRepository.create(RuntimeEnvironment.application);
|
||||||
|
|
||||||
|
long masterKeyId = mStaticRingDsa.getMasterKeyId();
|
||||||
|
Long authSubKeyId = keyRepository.getCachedPublicKeyRing(masterKeyId).getSecretAuthenticationId();
|
||||||
|
|
||||||
|
{ // sign challenge
|
||||||
|
AuthenticationOperation op = new AuthenticationOperation(RuntimeEnvironment.application,
|
||||||
|
keyRepository);
|
||||||
|
|
||||||
|
AuthenticationData.Builder authData = AuthenticationData.builder();
|
||||||
|
authData.setAuthenticationMasterKeyId(masterKeyId);
|
||||||
|
authData.setAuthenticationSubKeyId(authSubKeyId);
|
||||||
|
authData.setHashAlgorithm(HashAlgorithmTags.SHA256);
|
||||||
|
|
||||||
|
AuthenticationParcel authenticationParcel = AuthenticationParcel
|
||||||
|
.createAuthenticationParcel(authData.build(), challenge);
|
||||||
|
|
||||||
|
CryptoInputParcel inputParcel = CryptoInputParcel.createCryptoInputParcel();
|
||||||
|
inputParcel = inputParcel.withPassphrase(mKeyPhrase);
|
||||||
|
|
||||||
|
AuthenticationResult result = op.execute(authData.build(), inputParcel, authenticationParcel);
|
||||||
|
|
||||||
|
Assert.assertTrue("authentication must succeed", result.success());
|
||||||
|
|
||||||
|
signature = result.getSignature();
|
||||||
|
}
|
||||||
|
{ // verify signature
|
||||||
|
CanonicalizedPublicKey canonicalizedPublicKey = keyRepository.getCanonicalizedPublicKeyRing(masterKeyId)
|
||||||
|
.getPublicKey(authSubKeyId);
|
||||||
|
PublicKey publicKey = canonicalizedPublicKey.getJcaPublicKey();
|
||||||
|
|
||||||
|
Signature signatureVerifier = Signature.getInstance("SHA256withDSA");
|
||||||
|
signatureVerifier.initVerify(publicKey);
|
||||||
|
signatureVerifier.update(challenge);
|
||||||
|
boolean isSignatureValid = signatureVerifier.verify(signature);
|
||||||
|
|
||||||
|
Assert.assertTrue("signature must be valid", isSignatureValid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAccessControl() throws Exception {
|
public void testAccessControl() throws Exception {
|
||||||
|
|
||||||
@@ -152,7 +344,7 @@ public class AuthenticationOperationTest {
|
|||||||
|
|
||||||
KeyRepository keyRepository = KeyRepository.create(RuntimeEnvironment.application);
|
KeyRepository keyRepository = KeyRepository.create(RuntimeEnvironment.application);
|
||||||
|
|
||||||
long masterKeyId = mStaticRing.getMasterKeyId();
|
long masterKeyId = mStaticRingEcDsa.getMasterKeyId();
|
||||||
Long authSubKeyId = keyRepository.getCachedPublicKeyRing(masterKeyId).getSecretAuthenticationId();
|
Long authSubKeyId = keyRepository.getCachedPublicKeyRing(masterKeyId).getSecretAuthenticationId();
|
||||||
|
|
||||||
{ // sign challenge - should succeed with selected key allowed
|
{ // sign challenge - should succeed with selected key allowed
|
||||||
@@ -165,7 +357,7 @@ public class AuthenticationOperationTest {
|
|||||||
authData.setHashAlgorithm(HashAlgorithmTags.SHA512);
|
authData.setHashAlgorithm(HashAlgorithmTags.SHA512);
|
||||||
|
|
||||||
ArrayList<Long> allowedKeyIds = new ArrayList<>(1);
|
ArrayList<Long> allowedKeyIds = new ArrayList<>(1);
|
||||||
allowedKeyIds.add(mStaticRing.getMasterKeyId());
|
allowedKeyIds.add(mStaticRingEcDsa.getMasterKeyId());
|
||||||
authData.setAllowedAuthenticationKeyIds(allowedKeyIds);
|
authData.setAllowedAuthenticationKeyIds(allowedKeyIds);
|
||||||
|
|
||||||
AuthenticationParcel authenticationParcel = AuthenticationParcel
|
AuthenticationParcel authenticationParcel = AuthenticationParcel
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ import java.security.Security;
|
|||||||
@RunWith(KeychainTestRunner.class)
|
@RunWith(KeychainTestRunner.class)
|
||||||
public class SshPublicKeyTest {
|
public class SshPublicKeyTest {
|
||||||
|
|
||||||
private static UncachedKeyRing mStaticRing;
|
private static UncachedKeyRing mStaticRingEcDsa;
|
||||||
private static Passphrase mKeyPhrase;
|
private static Passphrase mKeyPhrase;
|
||||||
|
|
||||||
private static PrintStream oldShadowStream;
|
private static PrintStream oldShadowStream;
|
||||||
@@ -53,23 +53,8 @@ public class SshPublicKeyTest {
|
|||||||
oldShadowStream = ShadowLog.stream;
|
oldShadowStream = ShadowLog.stream;
|
||||||
// ShadowLog.stream = System.out;
|
// ShadowLog.stream = System.out;
|
||||||
|
|
||||||
/* keyring generation:
|
|
||||||
PgpKeyOperation op = new PgpKeyOperation(null);
|
|
||||||
SaveKeyringParcel.Builder builder = SaveKeyringParcel.buildNewKeyringParcel();
|
|
||||||
|
|
||||||
builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
|
|
||||||
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.CERTIFY_OTHER, 0L));
|
|
||||||
builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(
|
|
||||||
Algorithm.ECDSA, 0, SaveKeyringParcel.Curve.NIST_P256, KeyFlags.AUTHENTICATION, 0L));
|
|
||||||
builder.addUserId("blah");
|
|
||||||
builder.setNewUnlock(ChangeUnlockParcel.createUnLockParcelForNewKey(new Passphrase("x")));
|
|
||||||
|
|
||||||
PgpEditKeyResult result = op.createSecretKeyRing(builder.build());
|
|
||||||
new FileOutputStream("/tmp/authenticate.sec").write(result.getRing().getEncoded());
|
|
||||||
*/
|
|
||||||
|
|
||||||
mKeyPhrase = new Passphrase("x");
|
mKeyPhrase = new Passphrase("x");
|
||||||
mStaticRing = KeyringTestingHelper.readRingFromResource("/test-keys/authenticate.sec");
|
mStaticRingEcDsa = KeyringTestingHelper.readRingFromResource("/test-keys/authenticate_ecdsa.sec");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -80,7 +65,7 @@ public class SshPublicKeyTest {
|
|||||||
// don't log verbosely here, we're not here to test imports
|
// don't log verbosely here, we're not here to test imports
|
||||||
ShadowLog.stream = oldShadowStream;
|
ShadowLog.stream = oldShadowStream;
|
||||||
|
|
||||||
databaseInteractor.saveSecretKeyRing(mStaticRing);
|
databaseInteractor.saveSecretKeyRing(mStaticRingEcDsa);
|
||||||
|
|
||||||
// ok NOW log verbosely!
|
// ok NOW log verbosely!
|
||||||
ShadowLog.stream = System.out;
|
ShadowLog.stream = System.out;
|
||||||
@@ -90,7 +75,7 @@ public class SshPublicKeyTest {
|
|||||||
public void testECDSA() throws Exception {
|
public void testECDSA() throws Exception {
|
||||||
KeyRepository keyRepository = KeyRepository.create(RuntimeEnvironment.application);
|
KeyRepository keyRepository = KeyRepository.create(RuntimeEnvironment.application);
|
||||||
|
|
||||||
long masterKeyId = mStaticRing.getMasterKeyId();
|
long masterKeyId = mStaticRingEcDsa.getMasterKeyId();
|
||||||
long authSubKeyId = keyRepository.getCachedPublicKeyRing(masterKeyId).getSecretAuthenticationId();
|
long authSubKeyId = keyRepository.getCachedPublicKeyRing(masterKeyId).getSecretAuthenticationId();
|
||||||
CanonicalizedPublicKey canonicalizedPublicKey = keyRepository.getCanonicalizedPublicKeyRing(masterKeyId)
|
CanonicalizedPublicKey canonicalizedPublicKey = keyRepository.getCanonicalizedPublicKeyRing(masterKeyId)
|
||||||
.getPublicKey(authSubKeyId);
|
.getPublicKey(authSubKeyId);
|
||||||
|
|||||||
BIN
OpenKeychain/src/test/resources/test-keys/authenticate_dsa.sec
Normal file
BIN
OpenKeychain/src/test/resources/test-keys/authenticate_dsa.sec
Normal file
Binary file not shown.
BIN
OpenKeychain/src/test/resources/test-keys/authenticate_eddsa.sec
Normal file
BIN
OpenKeychain/src/test/resources/test-keys/authenticate_eddsa.sec
Normal file
Binary file not shown.
BIN
OpenKeychain/src/test/resources/test-keys/authenticate_rsa.sec
Normal file
BIN
OpenKeychain/src/test/resources/test-keys/authenticate_rsa.sec
Normal file
Binary file not shown.
@@ -0,0 +1,86 @@
|
|||||||
|
package org.bouncycastle.openpgp;
|
||||||
|
|
||||||
|
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
|
||||||
|
import org.bouncycastle.openpgp.operator.PGPContentSigner;
|
||||||
|
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generator for authentication signatures.
|
||||||
|
*/
|
||||||
|
public class AuthenticationSignatureGenerator {
|
||||||
|
private OutputStream sigOut;
|
||||||
|
private PGPContentSignerBuilder contentSignerBuilder;
|
||||||
|
private PGPContentSigner contentSigner;
|
||||||
|
private int sigType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a signature generator built on the passed in contentSignerBuilder.
|
||||||
|
*
|
||||||
|
* @param contentSignerBuilder builder to produce PGPContentSigner objects for generating signatures.
|
||||||
|
*/
|
||||||
|
public AuthenticationSignatureGenerator(PGPContentSignerBuilder contentSignerBuilder) {
|
||||||
|
this.contentSignerBuilder = contentSignerBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise the generator for signing.
|
||||||
|
*
|
||||||
|
* @param signatureType
|
||||||
|
* @param key
|
||||||
|
* @throws PGPException
|
||||||
|
*/
|
||||||
|
public void init(int signatureType, PGPPrivateKey key) throws PGPException {
|
||||||
|
contentSigner = contentSignerBuilder.build(signatureType, key);
|
||||||
|
sigOut = contentSigner.getOutputStream();
|
||||||
|
sigType = contentSigner.getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(byte b) {
|
||||||
|
byteUpdate(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(byte[] b) {
|
||||||
|
update(b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(byte[] b, int off, int len) {
|
||||||
|
blockUpdate(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void byteUpdate(byte b) {
|
||||||
|
try {
|
||||||
|
sigOut.write(b);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new PGPRuntimeOperationException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void blockUpdate(byte[] block, int off, int len) {
|
||||||
|
try {
|
||||||
|
sigOut.write(block, off, len);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new PGPRuntimeOperationException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the signature.
|
||||||
|
*
|
||||||
|
* @return byte[]
|
||||||
|
* @throws PGPException
|
||||||
|
*/
|
||||||
|
public byte[] getSignature() throws PGPException {
|
||||||
|
if (contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.RSA_SIGN
|
||||||
|
|| contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.RSA_GENERAL
|
||||||
|
|| contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.EDDSA
|
||||||
|
|| contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.ECDSA
|
||||||
|
|| contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.DSA) {
|
||||||
|
return contentSigner.getSignature();
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("Unsupported algorithm");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,201 +0,0 @@
|
|||||||
package org.bouncycastle.openpgp;
|
|
||||||
|
|
||||||
import org.bouncycastle.bcpg.MPInteger;
|
|
||||||
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
|
|
||||||
import org.bouncycastle.bcpg.SignaturePacket;
|
|
||||||
import org.bouncycastle.bcpg.SignatureSubpacket;
|
|
||||||
import org.bouncycastle.openpgp.operator.PGPContentSigner;
|
|
||||||
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
|
|
||||||
import org.bouncycastle.util.BigIntegers;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.math.BigInteger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generator for PGP Signatures.
|
|
||||||
*/
|
|
||||||
public class PGPAuthenticationSignatureGenerator
|
|
||||||
{
|
|
||||||
private OutputStream sigOut;
|
|
||||||
private PGPContentSignerBuilder contentSignerBuilder;
|
|
||||||
private PGPContentSigner contentSigner;
|
|
||||||
private int sigType;
|
|
||||||
private byte lastb;
|
|
||||||
private int providedKeyAlgorithm = -1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a signature generator built on the passed in contentSignerBuilder.
|
|
||||||
*
|
|
||||||
* @param contentSignerBuilder builder to produce PGPContentSigner objects for generating signatures.
|
|
||||||
*/
|
|
||||||
public PGPAuthenticationSignatureGenerator(
|
|
||||||
PGPContentSignerBuilder contentSignerBuilder)
|
|
||||||
{
|
|
||||||
this.contentSignerBuilder = contentSignerBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialise the generator for signing.
|
|
||||||
*
|
|
||||||
* @param signatureType
|
|
||||||
* @param key
|
|
||||||
* @throws PGPException
|
|
||||||
*/
|
|
||||||
public void init(
|
|
||||||
int signatureType,
|
|
||||||
PGPPrivateKey key)
|
|
||||||
throws PGPException
|
|
||||||
{
|
|
||||||
contentSigner = contentSignerBuilder.build(signatureType, key);
|
|
||||||
sigOut = contentSigner.getOutputStream();
|
|
||||||
sigType = contentSigner.getType();
|
|
||||||
lastb = 0;
|
|
||||||
|
|
||||||
if (providedKeyAlgorithm >= 0 && providedKeyAlgorithm != contentSigner.getKeyAlgorithm())
|
|
||||||
{
|
|
||||||
throw new PGPException("key algorithm mismatch");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void update(
|
|
||||||
byte b)
|
|
||||||
{
|
|
||||||
if (sigType == PGPSignature.CANONICAL_TEXT_DOCUMENT)
|
|
||||||
{
|
|
||||||
if (b == '\r')
|
|
||||||
{
|
|
||||||
byteUpdate((byte)'\r');
|
|
||||||
byteUpdate((byte)'\n');
|
|
||||||
}
|
|
||||||
else if (b == '\n')
|
|
||||||
{
|
|
||||||
if (lastb != '\r')
|
|
||||||
{
|
|
||||||
byteUpdate((byte)'\r');
|
|
||||||
byteUpdate((byte)'\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
byteUpdate(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
lastb = b;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
byteUpdate(b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void update(
|
|
||||||
byte[] b)
|
|
||||||
{
|
|
||||||
this.update(b, 0, b.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void update(
|
|
||||||
byte[] b,
|
|
||||||
int off,
|
|
||||||
int len)
|
|
||||||
{
|
|
||||||
if (sigType == PGPSignature.CANONICAL_TEXT_DOCUMENT)
|
|
||||||
{
|
|
||||||
int finish = off + len;
|
|
||||||
|
|
||||||
for (int i = off; i != finish; i++)
|
|
||||||
{
|
|
||||||
this.update(b[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
blockUpdate(b, off, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void byteUpdate(byte b)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
sigOut.write(b);
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
throw new PGPRuntimeOperationException(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void blockUpdate(byte[] block, int off, int len)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
sigOut.write(block, off, len);
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
throw new PGPRuntimeOperationException(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a signature object containing the current signature state.
|
|
||||||
*
|
|
||||||
* @return PGPSignature
|
|
||||||
* @throws PGPException
|
|
||||||
*/
|
|
||||||
public PGPSignature generate()
|
|
||||||
throws PGPException
|
|
||||||
{
|
|
||||||
MPInteger[] sigValues;
|
|
||||||
ByteArrayOutputStream sOut = new ByteArrayOutputStream();
|
|
||||||
SignatureSubpacket[] hPkts, unhPkts;
|
|
||||||
hPkts = new SignatureSubpacket[0];
|
|
||||||
unhPkts = new SignatureSubpacket[0];
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ByteArrayOutputStream hOut = new ByteArrayOutputStream();
|
|
||||||
byte[] data = hOut.toByteArray();
|
|
||||||
sOut.write(data);
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
throw new PGPException("exception encoding hashed data.", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] trailer = sOut.toByteArray();
|
|
||||||
|
|
||||||
blockUpdate(trailer, 0, trailer.length);
|
|
||||||
|
|
||||||
if (contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.RSA_SIGN
|
|
||||||
|| contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.RSA_GENERAL) // an RSA signature
|
|
||||||
{
|
|
||||||
sigValues = new MPInteger[1];
|
|
||||||
sigValues[0] = new MPInteger(new BigInteger(1, contentSigner.getSignature()));
|
|
||||||
}
|
|
||||||
else if (contentSigner.getKeyAlgorithm() == PublicKeyAlgorithmTags.EDDSA)
|
|
||||||
{
|
|
||||||
byte[] sig = contentSigner.getSignature();
|
|
||||||
|
|
||||||
sigValues = new MPInteger[2];
|
|
||||||
|
|
||||||
sigValues[0] = new MPInteger(BigIntegers.fromUnsignedByteArray(sig, 0, 32));
|
|
||||||
sigValues[1] = new MPInteger(BigIntegers.fromUnsignedByteArray(sig, 32, 32));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sigValues = PGPUtil.dsaSigToMpi(contentSigner.getSignature());
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] digest = contentSigner.getDigest();
|
|
||||||
byte[] fingerPrint = new byte[2];
|
|
||||||
|
|
||||||
fingerPrint[0] = digest[0];
|
|
||||||
fingerPrint[1] = digest[1];
|
|
||||||
|
|
||||||
return new PGPSignature(new SignaturePacket(sigType, contentSigner.getKeyID(), contentSigner.getKeyAlgorithm(), contentSigner.getHashAlgorithm(), hPkts, unhPkts, fingerPrint, sigValues));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package org.bouncycastle.openpgp.operator.jcajce;
|
||||||
|
|
||||||
|
import org.bouncycastle.jcajce.provider.asymmetric.eddsa.EdDSAEngine;
|
||||||
|
import org.bouncycastle.jcajce.provider.asymmetric.eddsa.spec.EdDSANamedCurveTable;
|
||||||
|
import org.bouncycastle.jcajce.provider.asymmetric.eddsa.spec.EdDSAParameterSpec;
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPPrivateKey;
|
||||||
|
import org.bouncycastle.openpgp.PGPRuntimeOperationException;
|
||||||
|
import org.bouncycastle.openpgp.operator.PGPContentSigner;
|
||||||
|
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.security.*;
|
||||||
|
|
||||||
|
public class EdDsaAuthenticationContentSignerBuilder implements PGPContentSignerBuilder {
|
||||||
|
private JcaPGPKeyConverter keyConverter = new JcaPGPKeyConverter();
|
||||||
|
private int hashAlgorithm;
|
||||||
|
private int keyAlgorithm;
|
||||||
|
|
||||||
|
public EdDsaAuthenticationContentSignerBuilder(int keyAlgorithm, int hashAlgorithm) {
|
||||||
|
this.keyAlgorithm = keyAlgorithm;
|
||||||
|
this.hashAlgorithm = hashAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EdDsaAuthenticationContentSignerBuilder setProvider(Provider provider) {
|
||||||
|
keyConverter.setProvider(provider);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EdDsaAuthenticationContentSignerBuilder setProvider(String providerName) {
|
||||||
|
keyConverter.setProvider(providerName);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Signature createSignature() throws NoSuchAlgorithmException {
|
||||||
|
EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("Ed25519");
|
||||||
|
return new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PGPContentSigner build(final int signatureType, final long keyID, final PrivateKey privateKey)
|
||||||
|
throws PGPException {
|
||||||
|
Signature signatureEdDsa;
|
||||||
|
try {
|
||||||
|
signatureEdDsa = createSignature();
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new PGPException("unable to create Signature.", e);
|
||||||
|
}
|
||||||
|
final Signature signature = signatureEdDsa;
|
||||||
|
|
||||||
|
final ByteArrayOutputStream dataOutputStream = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
try {
|
||||||
|
signature.initSign(privateKey);
|
||||||
|
} catch (InvalidKeyException e) {
|
||||||
|
throw new PGPException("invalid key.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PGPContentSigner() {
|
||||||
|
public int getType() {
|
||||||
|
return signatureType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHashAlgorithm() {
|
||||||
|
return hashAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getKeyAlgorithm() {
|
||||||
|
return keyAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getKeyID() {
|
||||||
|
return keyID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OutputStream getOutputStream() {
|
||||||
|
return new SignatureOutputStream(signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getSignature() {
|
||||||
|
try {
|
||||||
|
return signature.sign();
|
||||||
|
} catch (SignatureException e) {
|
||||||
|
throw new PGPRuntimeOperationException("Unable to create signature: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getDigest() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public PGPContentSigner build(final int signatureType, PGPPrivateKey privateKey) throws PGPException {
|
||||||
|
if (privateKey instanceof JcaPGPPrivateKey) {
|
||||||
|
return build(signatureType, privateKey.getKeyID(), ((JcaPGPPrivateKey) privateKey).getPrivateKey());
|
||||||
|
} else {
|
||||||
|
return build(signatureType, privateKey.getKeyID(), keyConverter.getPrivateKey(privateKey));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user