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:
Vincent Breitmoser
2017-11-25 15:32:05 +01:00
committed by GitHub
11 changed files with 438 additions and 265 deletions

View File

@@ -26,9 +26,10 @@ import java.security.interfaces.RSAPrivateCrtKey;
import java.util.Date;
import java.util.Map;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.bcpg.S2K;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.openpgp.PGPAuthenticationSignatureGenerator;
import org.bouncycastle.openpgp.AuthenticationSignatureGenerator;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPSecretKey;
@@ -37,13 +38,7 @@ import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.jcajce.CachingDataDecryptorFactory;
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.bouncycastle.openpgp.operator.jcajce.*;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
@@ -219,8 +214,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
return true;
}
private PGPContentSignerBuilder getContentSignerBuilder(int hashAlgo,
Map<ByteBuffer,byte[]> signedHashes) {
private PGPContentSignerBuilder getContentSignerBuilder(int hashAlgo, Map<ByteBuffer, byte[]> signedHashes) {
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
// use synchronous "NFC based" SignerBuilder
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 {
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
throw new PrivateKeyNotUnlockedException();
}
PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(hashAlgorithm, signedHashes);
PGPContentSignerBuilder contentSignerBuilder =
getAuthenticationContentSignerBuilder(hashAlgorithm, signedHashes);
try {
PGPAuthenticationSignatureGenerator signatureGenerator = new PGPAuthenticationSignatureGenerator(contentSignerBuilder);
AuthenticationSignatureGenerator signatureGenerator =
new AuthenticationSignatureGenerator(contentSignerBuilder);
signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, mPrivateKey);
return signatureGenerator;

View File

@@ -19,7 +19,7 @@ package org.sufficientlysecure.keychain.ssh;
import android.content.Context;
import android.support.annotation.NonNull;
import org.bouncycastle.openpgp.PGPAuthenticationSignatureGenerator;
import org.bouncycastle.openpgp.AuthenticationSignatureGenerator;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder;
import org.sufficientlysecure.keychain.operations.BaseOperation;
@@ -206,7 +206,7 @@ public class AuthenticationOperation extends BaseOperation<AuthenticationParcel>
}
PGPAuthenticationSignatureGenerator signatureGenerator;
AuthenticationSignatureGenerator signatureGenerator;
try {
signatureGenerator = authKey.getAuthenticationSignatureGenerator(
hashAlgorithm, cryptoInput.getCryptoData());
@@ -218,7 +218,7 @@ public class AuthenticationOperation extends BaseOperation<AuthenticationParcel>
signatureGenerator.update(challenge, 0, challenge.length);
try {
signature = signatureGenerator.generate().getSignature();
signature = signatureGenerator.getSignature();
} catch (NfcSyncPGPContentSignerBuilder.NfcInteractionNeeded e) {
// this secret key diverts to a OpenPGP card, thus requires user interaction
log.add(LogType.MSG_AUTH_PENDING_NFC, indent);

View File

@@ -20,6 +20,9 @@ package org.sufficientlysecure.keychain.operations;
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.junit.Assert;
import org.junit.Before;
@@ -42,6 +45,7 @@ import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.io.PrintStream;
import java.security.MessageDigest;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
@@ -50,35 +54,84 @@ import java.util.ArrayList;
@RunWith(KeychainTestRunner.class)
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 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
public static void setUpOnce() throws Exception {
Security.insertProviderAt(new BouncyCastleProvider(), 1);
oldShadowStream = ShadowLog.stream;
// 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");
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
@@ -89,21 +142,24 @@ public class AuthenticationOperationTest {
// don't log verbosely here, we're not here to test imports
ShadowLog.stream = oldShadowStream;
databaseInteractor.saveSecretKeyRing(mStaticRing);
databaseInteractor.saveSecretKeyRing(mStaticRingRsa);
databaseInteractor.saveSecretKeyRing(mStaticRingEcDsa);
databaseInteractor.saveSecretKeyRing(mStaticRingEdDsa);
databaseInteractor.saveSecretKeyRing(mStaticRingDsa);
// ok NOW log verbosely!
ShadowLog.stream = System.out;
}
@Test
public void testAuthenticate() throws Exception {
public void testAuthenticateRsa() throws Exception {
byte[] challenge = "dies ist ein challenge ☭".getBytes();
byte[] signature;
KeyRepository keyRepository = KeyRepository.create(RuntimeEnvironment.application);
long masterKeyId = mStaticRing.getMasterKeyId();
long masterKeyId = mStaticRingRsa.getMasterKeyId();
Long authSubKeyId = keyRepository.getCachedPublicKeyRing(masterKeyId).getSecretAuthenticationId();
{ // sign challenge
@@ -115,10 +171,6 @@ public class AuthenticationOperationTest {
authData.setAuthenticationSubKeyId(authSubKeyId);
authData.setHashAlgorithm(HashAlgorithmTags.SHA512);
// ArrayList<Long> allowedKeyIds = new ArrayList<>(1);
// allowedKeyIds.add(mStaticRing.getMasterKeyId());
// authData.setAllowedAuthenticationKeyIds(allowedKeyIds);
AuthenticationParcel authenticationParcel = AuthenticationParcel
.createAuthenticationParcel(authData.build(), challenge);
@@ -133,7 +185,53 @@ public class AuthenticationOperationTest {
}
{ // verify signature
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();
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
public void testAccessControl() throws Exception {
@@ -152,7 +344,7 @@ public class AuthenticationOperationTest {
KeyRepository keyRepository = KeyRepository.create(RuntimeEnvironment.application);
long masterKeyId = mStaticRing.getMasterKeyId();
long masterKeyId = mStaticRingEcDsa.getMasterKeyId();
Long authSubKeyId = keyRepository.getCachedPublicKeyRing(masterKeyId).getSecretAuthenticationId();
{ // sign challenge - should succeed with selected key allowed
@@ -165,7 +357,7 @@ public class AuthenticationOperationTest {
authData.setHashAlgorithm(HashAlgorithmTags.SHA512);
ArrayList<Long> allowedKeyIds = new ArrayList<>(1);
allowedKeyIds.add(mStaticRing.getMasterKeyId());
allowedKeyIds.add(mStaticRingEcDsa.getMasterKeyId());
authData.setAllowedAuthenticationKeyIds(allowedKeyIds);
AuthenticationParcel authenticationParcel = AuthenticationParcel

View File

@@ -42,7 +42,7 @@ import java.security.Security;
@RunWith(KeychainTestRunner.class)
public class SshPublicKeyTest {
private static UncachedKeyRing mStaticRing;
private static UncachedKeyRing mStaticRingEcDsa;
private static Passphrase mKeyPhrase;
private static PrintStream oldShadowStream;
@@ -53,23 +53,8 @@ public class SshPublicKeyTest {
oldShadowStream = ShadowLog.stream;
// 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");
mStaticRing = KeyringTestingHelper.readRingFromResource("/test-keys/authenticate.sec");
mStaticRingEcDsa = KeyringTestingHelper.readRingFromResource("/test-keys/authenticate_ecdsa.sec");
}
@Before
@@ -80,7 +65,7 @@ public class SshPublicKeyTest {
// don't log verbosely here, we're not here to test imports
ShadowLog.stream = oldShadowStream;
databaseInteractor.saveSecretKeyRing(mStaticRing);
databaseInteractor.saveSecretKeyRing(mStaticRingEcDsa);
// ok NOW log verbosely!
ShadowLog.stream = System.out;
@@ -90,7 +75,7 @@ public class SshPublicKeyTest {
public void testECDSA() throws Exception {
KeyRepository keyRepository = KeyRepository.create(RuntimeEnvironment.application);
long masterKeyId = mStaticRing.getMasterKeyId();
long masterKeyId = mStaticRingEcDsa.getMasterKeyId();
long authSubKeyId = keyRepository.getCachedPublicKeyRing(masterKeyId).getSecretAuthenticationId();
CanonicalizedPublicKey canonicalizedPublicKey = keyRepository.getCanonicalizedPublicKeyRing(masterKeyId)
.getPublicKey(authSubKeyId);

View File

@@ -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");
}
}
}

View File

@@ -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));
}
}

View File

@@ -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));
}
}
}