@Test
  public void shouldRoundTripBuildParseRpkiCaCertRequest()
      throws RpkiCaCertificateRequestParserException {

    RpkiCaCertificateRequestBuilder requestBuilder = new RpkiCaCertificateRequestBuilder();

    URI caRepositoryUri = URI.create("rsync://host/module/subdir/");
    URI manifestUri = URI.create("rsync://host/module/subdir/subject.mft");
    X500Principal subject = new X500Principal("CN=subject");
    KeyPair keyPair = PregeneratedKeyPairFactory.getInstance().generate();

    requestBuilder.withCaRepositoryUri(caRepositoryUri);
    requestBuilder.withManifestUri(manifestUri);
    requestBuilder.withSubject(subject);
    PKCS10CertificationRequest pkcs10Request = requestBuilder.build(keyPair);

    assertNotNull(pkcs10Request);

    RpkiCaCertificateRequestParser requestParser =
        new RpkiCaCertificateRequestParser(pkcs10Request);

    assertEquals(caRepositoryUri, requestParser.getCaRepositoryUri());
    assertEquals(manifestUri, requestParser.getManifestUri());
    assertEquals(keyPair.getPublic(), requestParser.getPublicKey());
  }
public class TopDownWalkerTest {

  private static final URI ROOT_SIA_REPO_RSYNC_LOCATION = URI.create("rsync://foo.host/bar/");
  private static final URI ROOT_SIA_MANIFEST_RSYNC_LOCATION =
      URI.create("rsync://foo.host/bar/manifest.mft");
  private static final URI ROOT_SIA_REPO_HTTP_LOCATION = URI.create("http://foo.host/bar/");

  // Trust anchor test data
  private static final X500Principal ROOT_CERTIFICATE_NAME =
      new X500Principal("CN=For Testing Only, CN=RIPE NCC, C=NL");
  private static final IpResourceSet ROOT_RESOURCE_SET =
      IpResourceSet.parse("10.0.0.0/8, 192.168.0.0/16, ffce::/16, AS21212");
  private static final BigInteger ROOT_SERIAL_NUMBER = BigInteger.valueOf(900);
  private static final ValidityPeriod VALIDITY_PERIOD =
      new ValidityPeriod(new DateTime().minusMinutes(1), new DateTime().plusYears(1));
  private static final KeyPair ROOT_KEY_PAIR = PregeneratedKeyPairFactory.getInstance().generate();

  // Manifest data
  public static final DateTime THIS_UPDATE_TIME =
      new DateTime(2008, 9, 1, 22, 43, 29, 0, DateTimeZone.UTC);
  public static final DateTime NEXT_UPDATE_TIME =
      new DateTime(2008, 9, 2, 6, 43, 29, 0, DateTimeZone.UTC);
  public static final byte[] FOO_CONTENT = {
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
    27, 28, 29, 30, 31, 32
  };

  public static final byte[] BAR_CONTENT = {
    32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9,
    8, 7, 6, 5, 4, 3, 2, 1
  };

  private Queue<CertificateRepositoryObjectValidationContext> workQueue;
  private TopDownWalker subject;
  private CertificateRepositoryObjectFetcher certificateRepositoryObjectFetcher;
  private X509ResourceCertificate ta;
  private CertificateRepositoryObjectValidationContext taContext;
  private ValidationResult validitionResult;

  @Before
  public void setUp() {
    certificateRepositoryObjectFetcher = mock(CertificateRepositoryObjectFetcher.class);

    ta = getRootResourceCertificate();
    taContext = new CertificateRepositoryObjectValidationContext(URI.create("rsync://host/ta"), ta);
    workQueue = new LinkedList<CertificateRepositoryObjectValidationContext>();
    validitionResult = ValidationResult.withLocation("ta.cer");
    subject = new TopDownWalker(workQueue, certificateRepositoryObjectFetcher, validitionResult);
  }

  @Test
  public void shouldPrefetchRepository() {
    subject.prefetch(taContext);

    verify(certificateRepositoryObjectFetcher)
        .prefetch(eq(ROOT_SIA_REPO_RSYNC_LOCATION), isA(ValidationResult.class));
  }

  @Test
  public void shouldProcessManifestFromObjectIssuingCertificate() {
    final ManifestCms manifestCms = getRootManifestCms();
    final MutableBoolean fetchManifestCalled = new MutableBoolean(false);
    final MutableBoolean processedManifestFilesCalled = new MutableBoolean(false);

    subject =
        new TopDownWalker(workQueue, certificateRepositoryObjectFetcher, validitionResult) {
          @Override
          ManifestCms fetchManifest(
              URI manifestURI, CertificateRepositoryObjectValidationContext context) {
            fetchManifestCalled.setValue(true);
            assertEquals(ta.getManifestUri(), manifestURI);
            return manifestCms;
          }

          @Override
          void processManifestFiles(
              CertificateRepositoryObjectValidationContext context, ManifestCms actualManifestCms) {
            processedManifestFilesCalled.setValue(true);
            assertEquals(ta, context.getCertificate());
            assertEquals(manifestCms, actualManifestCms);
          }
        };

    subject.processManifest(taContext);

    assertTrue(fetchManifestCalled.booleanValue());
    assertTrue(processedManifestFilesCalled.booleanValue());
  }

  @Test
  public void shouldNotProcessFilesWhenManifestIsNull() {
    when(certificateRepositoryObjectFetcher.getManifest(
            eq(ROOT_SIA_MANIFEST_RSYNC_LOCATION), eq(taContext), isA(ValidationResult.class)))
        .thenReturn(null);

    subject.processManifest(taContext);

    verify(certificateRepositoryObjectFetcher)
        .getManifest(
            eq(ROOT_SIA_MANIFEST_RSYNC_LOCATION), eq(taContext), isA(ValidationResult.class));
    verifyNoMoreInteractions(certificateRepositoryObjectFetcher);
  }

  @Test
  public void shouldFetchManifest() {
    ManifestCms manifestCms = getRootManifestCms();
    when(certificateRepositoryObjectFetcher.getManifest(
            eq(ROOT_SIA_MANIFEST_RSYNC_LOCATION), eq(taContext), isA(ValidationResult.class)))
        .thenReturn(manifestCms);

    assertEquals(manifestCms, subject.fetchManifest(ROOT_SIA_MANIFEST_RSYNC_LOCATION, taContext));
  }

  @Test
  public void shouldAddObjectIssuerCertificatesToWorkQueue() {
    subject.addToWorkQueueIfObjectIssuer(taContext, taContext.getLocation(), ta);

    assertTrue(workQueue.size() == 1);
    CertificateRepositoryObjectValidationContext context = workQueue.remove();
    assertEquals(ta, context.getCertificate());
  }

  @Test
  public void shouldSkipNotObjectIssuerCertificateObjects() {
    X509ResourceCertificate certificate = createManifestEECertificate();
    X509Crl crl = getCrl();

    subject.addToWorkQueueIfObjectIssuer(taContext, URI.create("rsync://host/cert"), certificate);
    assertTrue(workQueue.isEmpty());

    subject.addToWorkQueueIfObjectIssuer(taContext, URI.create("rsync://host/crl"), crl);
    assertTrue(workQueue.isEmpty());
  }

  @Test
  public void shouldAddFetchedObjectIssuerToWorkQueue() {
    ManifestCms manifestCms = getRootManifestCms();
    X509Crl crl = getCrl();

    when(certificateRepositoryObjectFetcher.getObject(
            eq(ROOT_SIA_REPO_RSYNC_LOCATION.resolve("foo1")),
            eq(taContext),
            eq(manifestCms.getFileContentSpecification("foo1")),
            isA(ValidationResult.class)))
        .thenReturn(ta);
    when(certificateRepositoryObjectFetcher.getObject(
            eq(ROOT_SIA_REPO_RSYNC_LOCATION.resolve("BaR")),
            eq(taContext),
            eq(manifestCms.getFileContentSpecification("BaR")),
            isA(ValidationResult.class)))
        .thenReturn(crl);

    subject.processManifestFiles(taContext, manifestCms);

    assertEquals(1, workQueue.size());
    assertEquals(ta, workQueue.remove().getCertificate());
  }

  @Test
  public void shouldSkipInvalidObjects() {
    ManifestCms manifestCms = getRootManifestCms();

    when(certificateRepositoryObjectFetcher.getObject(
            eq(ROOT_SIA_REPO_RSYNC_LOCATION.resolve("foo1")),
            eq(taContext),
            eq(manifestCms.getFileContentSpecification("foo1")),
            isA(ValidationResult.class)))
        .thenReturn(null);
    when(certificateRepositoryObjectFetcher.getObject(
            eq(ROOT_SIA_REPO_RSYNC_LOCATION.resolve("BaR")),
            eq(taContext),
            eq(manifestCms.getFileContentSpecification("BaR")),
            isA(ValidationResult.class)))
        .thenReturn(null);

    subject.processManifestFiles(taContext, manifestCms);

    assertTrue(workQueue.isEmpty());
  }

  public static ManifestCms getRootManifestCms() {
    ManifestCmsBuilder builder = new ManifestCmsBuilder();
    builder
        .withCertificate(createManifestEECertificate())
        .withManifestNumber(BigInteger.valueOf(68));
    builder.withThisUpdateTime(THIS_UPDATE_TIME).withNextUpdateTime(NEXT_UPDATE_TIME);
    builder.addFile("foo1", FOO_CONTENT);
    builder.addFile("BaR", BAR_CONTENT);
    builder.withSignatureProvider(DEFAULT_SIGNATURE_PROVIDER);
    return builder.build(ROOT_KEY_PAIR.getPrivate());
  }

  static X509ResourceCertificate createManifestEECertificate() {
    X509ResourceCertificateBuilder builder = new X509ResourceCertificateBuilder();
    builder
        .withCa(false)
        .withSubjectDN(ROOT_CERTIFICATE_NAME)
        .withIssuerDN(ROOT_CERTIFICATE_NAME)
        .withSerial(BigInteger.ONE);
    builder.withPublicKey(ROOT_KEY_PAIR.getPublic());
    builder.withSigningKeyPair(ROOT_KEY_PAIR);
    builder.withInheritedResourceTypes(EnumSet.allOf(IpResourceType.class));
    builder.withValidityPeriod(new ValidityPeriod(THIS_UPDATE_TIME, NEXT_UPDATE_TIME));
    return builder.build();
  }

  static X509ResourceCertificate getRootResourceCertificate() {
    X509ResourceCertificateBuilder builder = new X509ResourceCertificateBuilder();

    builder.withSubjectDN(ROOT_CERTIFICATE_NAME);
    builder.withIssuerDN(ROOT_CERTIFICATE_NAME);
    builder.withSerial(ROOT_SERIAL_NUMBER);
    builder.withValidityPeriod(VALIDITY_PERIOD);
    builder.withPublicKey(ROOT_KEY_PAIR.getPublic());
    builder.withCa(true);
    builder.withKeyUsage(KeyUsage.keyCertSign);
    builder.withAuthorityKeyIdentifier(true);
    builder.withSubjectKeyIdentifier(true);
    builder.withResources(ROOT_RESOURCE_SET);
    builder.withAuthorityKeyIdentifier(false);
    builder.withSigningKeyPair(ROOT_KEY_PAIR);

    X509CertificateInformationAccessDescriptor[] descriptors = {
      new X509CertificateInformationAccessDescriptor(
          X509CertificateInformationAccessDescriptor.ID_AD_CA_REPOSITORY,
          ROOT_SIA_REPO_HTTP_LOCATION),
      new X509CertificateInformationAccessDescriptor(
          X509CertificateInformationAccessDescriptor.ID_AD_CA_REPOSITORY,
          ROOT_SIA_REPO_RSYNC_LOCATION),
      new X509CertificateInformationAccessDescriptor(
          X509CertificateInformationAccessDescriptor.ID_AD_RPKI_MANIFEST,
          ROOT_SIA_MANIFEST_RSYNC_LOCATION),
    };
    builder.withSubjectInformationAccess(descriptors);

    return builder.build();
  }

  private X509Crl getCrl() {
    X509CrlBuilder builder = new X509CrlBuilder();
    builder.withIssuerDN(new X500Principal("CN=issuer"));
    builder.withThisUpdateTime(new DateTime());
    builder.withNextUpdateTime(new DateTime().plusHours(8));
    builder.withNumber(BigInteger.TEN);
    builder.withAuthorityKeyIdentifier(ROOT_KEY_PAIR.getPublic());
    return builder.build(ROOT_KEY_PAIR.getPrivate());
  }
}