diff options
Diffstat (limited to 'javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java')
-rw-r--r-- | javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java b/javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java new file mode 100644 index 0000000000..266f8684fa --- /dev/null +++ b/javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java @@ -0,0 +1,204 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.gpg; + +import static com.google.gerrit.gpg.PublicKeyStore.keyIdToString; +import static com.google.gerrit.gpg.PublicKeyStore.keyToString; +import static com.google.gerrit.gpg.testing.TestKeys.expiredKey; +import static com.google.gerrit.gpg.testing.TestKeys.validKeyWithExpiration; +import static com.google.gerrit.gpg.testing.TestKeys.validKeyWithoutExpiration; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.Assert.assertEquals; + +import com.google.gerrit.gpg.testing.TestKey; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import org.bouncycastle.bcpg.ArmoredOutputStream; +import org.bouncycastle.bcpg.BCPGOutputStream; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPSignatureGenerator; +import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.bouncycastle.openpgp.PGPUtil; +import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder; +import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription; +import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; +import org.eclipse.jgit.lib.CommitBuilder; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.PushCertificate; +import org.eclipse.jgit.transport.PushCertificateIdent; +import org.eclipse.jgit.transport.PushCertificateParser; +import org.eclipse.jgit.transport.SignedPushConfig; +import org.junit.Before; +import org.junit.Test; + +public class PushCertificateCheckerTest { + private InMemoryRepository repo; + private PublicKeyStore store; + private SignedPushConfig signedPushConfig; + private PushCertificateChecker checker; + + @Before + public void setUp() throws Exception { + TestKey key1 = validKeyWithoutExpiration(); + TestKey key3 = expiredKey(); + repo = new InMemoryRepository(new DfsRepositoryDescription("repo")); + store = new PublicKeyStore(repo); + store.add(key1.getPublicKeyRing()); + store.add(key3.getPublicKeyRing()); + + PersonIdent ident = new PersonIdent("A U Thor", "author@example.com"); + CommitBuilder cb = new CommitBuilder(); + cb.setAuthor(ident); + cb.setCommitter(ident); + assertEquals(RefUpdate.Result.NEW, store.save(cb)); + + signedPushConfig = new SignedPushConfig(); + signedPushConfig.setCertNonceSeed("sekret"); + signedPushConfig.setCertNonceSlopLimit(60 * 24); + checker = newChecker(true); + } + + private PushCertificateChecker newChecker(boolean checkNonce) { + PublicKeyChecker keyChecker = new PublicKeyChecker().setStore(store); + return new PushCertificateChecker(keyChecker) { + @Override + protected Repository getRepository() { + return repo; + } + + @Override + protected boolean shouldClose(Repository repo) { + return false; + } + }.setCheckNonce(checkNonce); + } + + @Test + public void validCert() throws Exception { + PushCertificate cert = newSignedCert(validNonce(), validKeyWithoutExpiration()); + assertNoProblems(cert); + } + + @Test + public void invalidNonce() throws Exception { + PushCertificate cert = newSignedCert("invalid-nonce", validKeyWithoutExpiration()); + assertProblems(cert, "Invalid nonce"); + } + + @Test + public void invalidNonceNotChecked() throws Exception { + checker = newChecker(false); + PushCertificate cert = newSignedCert("invalid-nonce", validKeyWithoutExpiration()); + assertNoProblems(cert); + } + + @Test + public void missingKey() throws Exception { + TestKey key2 = validKeyWithExpiration(); + PushCertificate cert = newSignedCert(validNonce(), key2); + assertProblems(cert, "No public keys found for key ID " + keyIdToString(key2.getKeyId())); + } + + @Test + public void invalidKey() throws Exception { + TestKey key3 = expiredKey(); + PushCertificate cert = newSignedCert(validNonce(), key3); + assertProblems( + cert, "Invalid public key " + keyToString(key3.getPublicKey()) + ":\n Key is expired"); + } + + @Test + public void signatureByExpiredKeyBeforeExpiration() throws Exception { + TestKey key3 = expiredKey(); + Date now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z").parse("2005-07-10 12:00:00 -0400"); + PushCertificate cert = newSignedCert(validNonce(), key3, now); + assertNoProblems(cert); + } + + private String validNonce() { + return signedPushConfig + .getNonceGenerator() + .createNonce(repo, System.currentTimeMillis() / 1000); + } + + private PushCertificate newSignedCert(String nonce, TestKey signingKey) throws Exception { + return newSignedCert(nonce, signingKey, null); + } + + private PushCertificate newSignedCert(String nonce, TestKey signingKey, Date now) + throws Exception { + PushCertificateIdent ident = + new PushCertificateIdent(signingKey.getFirstUserId(), System.currentTimeMillis(), -7 * 60); + String payload = + "certificate version 0.1\n" + + "pusher " + + ident.getRaw() + + "\n" + + "pushee test://localhost/repo.git\n" + + "nonce " + + nonce + + "\n" + + "\n" + + "0000000000000000000000000000000000000000" + + " deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" + + " refs/heads/master\n"; + PGPSignatureGenerator gen = + new PGPSignatureGenerator( + new BcPGPContentSignerBuilder(signingKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1)); + + if (now != null) { + PGPSignatureSubpacketGenerator subGen = new PGPSignatureSubpacketGenerator(); + subGen.setSignatureCreationTime(false, now); + gen.setHashedSubpackets(subGen.generate()); + } + + gen.init(PGPSignature.BINARY_DOCUMENT, signingKey.getPrivateKey()); + gen.update(payload.getBytes(UTF_8)); + PGPSignature sig = gen.generate(); + + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + try (BCPGOutputStream out = new BCPGOutputStream(new ArmoredOutputStream(bout))) { + sig.encode(out); + } + + String cert = payload + new String(bout.toByteArray(), UTF_8); + Reader reader = new InputStreamReader(new ByteArrayInputStream(cert.getBytes(UTF_8)), UTF_8); + PushCertificateParser parser = new PushCertificateParser(repo, signedPushConfig); + return parser.parse(reader); + } + + private void assertProblems(PushCertificate cert, String first, String... rest) throws Exception { + List<String> expected = new ArrayList<>(); + expected.add(first); + expected.addAll(Arrays.asList(rest)); + CheckResult result = checker.check(cert).getCheckResult(); + assertEquals(expected, result.getProblems()); + } + + private void assertNoProblems(PushCertificate cert) { + CheckResult result = checker.check(cert).getCheckResult(); + assertEquals(Collections.emptyList(), result.getProblems()); + } +} |