/*
 * Copyright 2019 the original author or authors.
 *
 * 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 org.gradle.integtests.resolve.verification

import org.gradle.api.internal.artifacts.ivyservice.CacheLayout
import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
import org.gradle.integtests.fixtures.UnsupportedWithConfigurationCache
import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
import org.gradle.security.fixtures.KeyServer
import org.gradle.security.fixtures.SigningFixtures
import org.gradle.security.internal.Fingerprint
import spock.lang.Issue

import static org.gradle.security.fixtures.SigningFixtures.getValidPublicKeyLongIdHexString
import static org.gradle.security.fixtures.SigningFixtures.signAsciiArmored
import static org.gradle.security.fixtures.SigningFixtures.validPublicKeyHexString
import static org.gradle.security.internal.SecuritySupport.toLongIdHexString
import static org.gradle.util.Matchers.containsText

class DependencyVerificationSignatureCheckIntegTest extends AbstractSignatureVerificationIntegrationTest {

    def "doesn't need checksums if signature is verified"() {
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKey("org:foo:1.0", validPublicKeyHexString)
            addTrustedKey("org:foo:1.0", validPublicKeyHexString, "pom", "pom")
        }

        given:
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                signAsciiArmored(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        serveValidKey()

        then:
        succeeds ":compileJava"
    }

    def "doesn't need checksums if signature is verified and trust using long id"() {
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKey("org:foo:1.0", validPublicKeyLongIdHexString)
            addTrustedKey("org:foo:1.0", validPublicKeyLongIdHexString, "pom", "pom")
        }

        given:
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                signAsciiArmored(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        serveValidKey()

        then:
        succeeds ":compileJava"
    }

    def "if signature is verified and checksum is declared in configuration, verify checksum (terse output=#terse)"() {
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKey("org:foo:1.0", validPublicKeyHexString)
            addTrustedKey("org:foo:1.0", validPublicKeyHexString, "pom", "pom")
            addChecksum("org:foo:1.0", "sha256", "invalid")
        }

        given:
        terseConsoleOutput(terse)
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                signAsciiArmored(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        serveValidKey()
        fails ":compileJava"

        then:

        assertVerificationError(terse) {
            whenTerse """Dependency verification failed for configuration ':compileClasspath'
One artifact failed verification: foo-1.0.jar (org:foo:1.0) from repository maven
This can indicate that a dependency has been compromised. Please carefully verify the checksums."""

            whenVerbose """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': expected a 'sha256' checksum of 'invalid' but was '20ae575ede776e5e06ee6b168652d11ee23069e92de110fdec13fbeaa5cf3bbc'

This can indicate that a dependency has been compromised. Please carefully verify the checksums."""
        }
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }

        where:
        terse << [true, false]
    }

    def "fails verification if key is (terse output=#terse)"() {
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKey("org:foo:1.0", validPublicKeyHexString)
            addTrustedKey("org:foo:1.0", validPublicKeyHexString, "pom", "pom")
        }

        given:
        terseConsoleOutput(terse)
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                signAsciiArmored(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        fails ":compileJava"

        then:
        assertVerificationError(terse) {
            whenTerse """Dependency verification failed for configuration ':compileClasspath'
2 artifacts failed verification:
  - foo-1.0.jar (org:foo:1.0) from repository maven
  - foo-1.0.pom (org:foo:1.0) from repository maven
This can indicate that a dependency has been compromised. Please carefully verify the signatures and checksums."""

            whenVerbose """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key '14f53f0824875d73' but it wasn't found in any key server so it couldn't be verified
  - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': Artifact was signed with key '14f53f0824875d73' but it wasn't found in any key server so it couldn't be verified

This can indicate that a dependency has been compromised. Please carefully verify the signatures and checksums."""
        }
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }

        where:
        terse << [true, false]
    }

    @ToBeFixedForConfigurationCache
    def "can verify signature for artifacts downloaded in a previous build (stop in between = #stopInBetween)"() {
        given:
        terseConsoleOutput(false)
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                signAsciiArmored(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        expect:
        succeeds ":compileJava"

        when:
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKey("org:foo:1.0", validPublicKeyHexString)
            addTrustedKey("org:foo:1.0", validPublicKeyHexString, "pom", "pom")
        }
        if (stopInBetween) {
            executer.stop()
        }
        fails ":compileJava"

        then:
        failure.assertHasCause """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key '14f53f0824875d73' but it wasn't found in any key server so it couldn't be verified
  - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': Artifact was signed with key '14f53f0824875d73' but it wasn't found in any key server so it couldn't be verified

This can indicate that a dependency has been compromised. Please carefully verify the signatures and checksums."""

        where:
        stopInBetween << [false, true]
    }

    @ToBeFixedForConfigurationCache
    def "can verify classified artifacts downloaded in previous builds (stop in between = #stopInBetween)"() {
        def keyring = newKeyRing()
        keyServerFixture.registerPublicKey(keyring.publicKey)
        def pkId = Fingerprint.of(keyring.publicKey)

        given:
        terseConsoleOutput(false)
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            artifact(classifier: 'classy')
            withSignature {
                keyring.sign(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0:classy"
            }
        """

        expect:
        succeeds ":compileJava"

        when:
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKeyByFileName("org:foo:1.0", "foo-1.0-classy.jar", validPublicKeyHexString)
            addTrustedKey("org:foo:1.0", validPublicKeyHexString, "pom", "pom")
        }
        if (stopInBetween) {
            executer.stop()
        }
        fails ":compileJava"

        then:
        failure.assertHasCause """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0-classy.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key '$pkId' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.
  - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': Artifact was signed with key '$pkId' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.

If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file by following the instructions at ${docsUrl}"""

        where:
        stopInBetween << [false, true]
    }

    def "fails verification if signature is incorrect (terse output=#terse)"() {
        createMetadataFile {
            noMetadataVerification()
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKey("org:foo:1.0", validPublicKeyHexString)
        }

        given:
        terseConsoleOutput(terse)
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                signAsciiArmored(it)
                if (name.endsWith(".jar")) {
                    // change contents of original file so that the signature doesn't match anymore
                    tamperWithFile(it)
                }
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        serveValidKey()
        fails ":compileJava"

        then:
        assertVerificationError(terse) {
            whenTerse """Dependency verification failed for configuration ':compileClasspath'
One artifact failed verification: foo-1.0.jar (org:foo:1.0) from repository maven
This can indicate that a dependency has been compromised. Please carefully verify the signatures and checksums."""

            whenVerbose """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key 'd7bf96a169f77b28c934ab1614f53f0824875d73' (Gradle Test (This is used for testing the gradle-signing-plugin) <test@gradle.org>) but signature didn't match

This can indicate that a dependency has been compromised. Please carefully verify the signatures and checksums."""
        }
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }

        where:
        terse << [true, false]
    }

    def "doesn't check the same artifact multiple times during a build (terse output=#terse)"() {
        createMetadataFile {
            noMetadataVerification()
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKey("org:foo:1.0", validPublicKeyHexString)
            trust("org", "bar", "1.0")
        }

        given:
        terseConsoleOutput(terse)
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                signAsciiArmored(it)
                if (name.endsWith(".jar")) {
                    // change contents of original file so that the signature doesn't match anymore
                    tamperWithFile(it)
                }
            }
        }
        uncheckedModule("org", "bar", "1.0") {
            dependsOn("org", "foo", "1.0")
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
                implementation "org:bar:1.0"
            }
        """

        when:
        serveValidKey()
        fails ":compileJava"

        then:
        assertVerificationError(terse) {
            whenTerse """Dependency verification failed for configuration ':compileClasspath'
One artifact failed verification: foo-1.0.jar (org:foo:1.0) from repository maven
This can indicate that a dependency has been compromised. Please carefully verify the signatures and checksums."""

            whenVerbose """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key 'd7bf96a169f77b28c934ab1614f53f0824875d73' (Gradle Test (This is used for testing the gradle-signing-plugin) <test@gradle.org>) but signature didn't match

This can indicate that a dependency has been compromised. Please carefully verify the signatures and checksums."""
        }
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }

        where:
        terse << [true, false]
    }

    def "doesn't check the same parent POM file multiple times during a build  (terse output=#terse)"() {
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKey("org:foo:1.0", validPublicKeyHexString)
            addTrustedKey("org:foo:1.0", validPublicKeyHexString, "pom", "pom")
            trust("org", "bar", "1.0")
        }

        given:
        terseConsoleOutput(terse)
        javaLibrary()
        uncheckedModule("org", "parent", "1.0") {
            withSignature {
                signAsciiArmored(it)
                if (name.endsWith(".pom")) {
                    // change contents of original file so that the signature doesn't match anymore
                    text = "${text}\n"
                }
            }
        }
        uncheckedModule("org", "foo", "1.0") {
            parent("org", "parent", "1.0")
            withSignature {
                signAsciiArmored(it)
            }
        }
        uncheckedModule("org", "bar", "1.0") {
            parent("org", "parent", "1.0")
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
                implementation "org:bar:1.0"
            }
        """

        when:
        serveValidKey()
        fails ":compileJava"

        then:
        assertVerificationError(terse) {
            whenTerse """Dependency verification failed for configuration ':compileClasspath'
One artifact failed verification: parent-1.0.pom (org:parent:1.0) from repository maven
This can indicate that a dependency has been compromised. Please carefully verify the signatures and checksums."""

            whenVerbose """Dependency verification failed for configuration ':compileClasspath':
  - On artifact parent-1.0.pom (org:parent:1.0) in repository 'maven': Artifact was signed with key 'd7bf96a169f77b28c934ab1614f53f0824875d73' (Gradle Test (This is used for testing the gradle-signing-plugin) <test@gradle.org>) but signature didn't match

This can indicate that a dependency has been compromised. Please carefully verify the signatures and checksums."""
        }
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }

        where:
        terse << [true, false]
    }

    def "fails verification if signature is not trusted (terse output=#terse)"() {
        def keyring = newKeyRing()
        keyServerFixture.registerPublicKey(keyring.publicKey)
        def pkId = Fingerprint.of(keyring.publicKey)

        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKey("org:foo:1.0", validPublicKeyHexString)
            addTrustedKey("org:foo:1.0", validPublicKeyHexString, "pom", "pom")
        }

        given:
        terseConsoleOutput(terse)
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                keyring.sign(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        fails ":compileJava"

        then:
        assertVerificationError(terse) {
            whenTerse """Dependency verification failed for configuration ':compileClasspath'
2 artifacts failed verification:
  - foo-1.0.jar (org:foo:1.0) from repository maven
  - foo-1.0.pom (org:foo:1.0) from repository maven"""

            whenVerbose """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key '$pkId' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.
  - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': Artifact was signed with key '$pkId' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.

If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file by following the instructions at ${docsUrl}"""
        }
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }

        where:
        terse << [true, false]
    }

    def "can verify classified artifacts trusting key #trustedKey"() {
        def keyring = newKeyRing()
        keyServerFixture.registerPublicKey(keyring.publicKey)
        def pkId = Fingerprint.of(keyring.publicKey)

        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKeyByFileName("org:foo:1.0", "foo-1.0-classy.jar", trustedKey)
            addTrustedKey("org:foo:1.0", trustedKey, "pom", "pom")
        }

        given:
        terseConsoleOutput(false)
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            artifact(classifier: 'classy')
            withSignature {
                keyring.sign(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0:classy"
            }
        """

        when:
        fails ":compileJava"

        then:
        failure.assertHasCause """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0-classy.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key '$pkId' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.
  - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': Artifact was signed with key '$pkId' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.

If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file by following the instructions at ${docsUrl}"""
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }

        where:
        trustedKey << [
            validPublicKeyHexString,
            validPublicKeyLongIdHexString
        ]
    }

    def "reasonable error message if key server fails to answer (terse output=#terse)"() {
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKey("org:foo:1.0", validPublicKeyHexString)
            addTrustedKey("org:foo:1.0", validPublicKeyHexString, "pom", "pom")
        }

        given:
        terseConsoleOutput(terse)
        keyServerFixture.stop()
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                signAsciiArmored(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        fails ":compileJava"

        then:
        assertVerificationError(terse) {
            whenTerse """Dependency verification failed for configuration ':compileClasspath'
2 artifacts failed verification:
  - foo-1.0.jar (org:foo:1.0) from repository maven
  - foo-1.0.pom (org:foo:1.0) from repository maven"""

            whenVerbose """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key '14f53f0824875d73' but it wasn't found in any key server so it couldn't be verified
  - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': Artifact was signed with key '14f53f0824875d73' but it wasn't found in any key server so it couldn't be verified

This can indicate that a dependency has been compromised. Please carefully verify the signatures and checksums."""
        }
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }

        where:
        terse << [true, false]
    }

    def "can fetch key from different keyserver"() {
        def keyring = newKeyRing()
        def secondServer = new KeyServer(temporaryFolder.createDir("keyserver-${UUID.randomUUID()}"))
        secondServer.registerPublicKey(keyring.publicKey)
        def pkId = toLongIdHexString(keyring.publicKey.keyID)
        secondServer.start()
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            keyServer(secondServer.uri)
            verifySignatures()
            addTrustedKey("org:foo:1.0", pkId)
            addTrustedKey("org:foo:1.0", pkId, "pom", "pom")
        }

        given:
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                keyring.sign(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        expect:
        succeeds ":compileJava"

        cleanup:
        secondServer.stop()
    }

    def "must verify all signatures (terse output=#terse)"() {
        def keyring = newKeyRing()
        keyServerFixture.withDefaultSigningKey()
        keyServerFixture.registerPublicKey(keyring.publicKey)
        def pkId = Fingerprint.of(keyring.publicKey)

        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKey("org:foo:1.0", validPublicKeyHexString)
            addTrustedKey("org:foo:1.0", validPublicKeyHexString, "pom", "pom")
        }

        given:
        terseConsoleOutput(terse)
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                keyring.sign(it, [(SigningFixtures.validSecretKey): SigningFixtures.validPassword])
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        fails ":compileJava"

        then:
        assertVerificationError(terse) {
            whenTerse """Dependency verification failed for configuration ':compileClasspath'
2 artifacts failed verification:
  - foo-1.0.jar (org:foo:1.0) from repository maven
  - foo-1.0.pom (org:foo:1.0) from repository maven"""

            whenVerbose """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key '$pkId' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.
  - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': Artifact was signed with key '$pkId' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.

If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file by following the instructions at ${docsUrl}"""
        }
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }

        where:
        terse << [true, false]
    }

    def "caches missing keys (terse output=#terse)"() {
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKey("org:foo:1.0", SigningFixtures.validPublicKeyHexString)
            addTrustedKey("org:foo:1.0", SigningFixtures.validPublicKeyHexString, "pom", "pom")
        }

        given:
        terseConsoleOutput(terse)
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                signAsciiArmored(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        fails ":compileJava"

        then:
        assertVerificationError(terse) {
            whenTerse """Dependency verification failed for configuration ':compileClasspath'
2 artifacts failed verification:
  - foo-1.0.jar (org:foo:1.0) from repository maven
  - foo-1.0.pom (org:foo:1.0) from repository maven"""

            whenVerbose """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key '14f53f0824875d73' but it wasn't found in any key server so it couldn't be verified
  - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': Artifact was signed with key '14f53f0824875d73' but it wasn't found in any key server so it couldn't be verified

This can indicate that a dependency has been compromised. Please carefully verify the signatures and checksums."""
        }
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }

        when: "publish keys"
        keyServerFixture.withDefaultSigningKey()
        fails ":compileJava"

        then:
        assertVerificationError(terse) {
            whenTerse """Dependency verification failed for configuration ':compileClasspath'
2 artifacts failed verification:
  - foo-1.0.jar (org:foo:1.0) from repository maven
  - foo-1.0.pom (org:foo:1.0) from repository maven"""

            whenVerbose """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key '14f53f0824875d73' but it wasn't found in any key server so it couldn't be verified
  - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': Artifact was signed with key '14f53f0824875d73' but it wasn't found in any key server so it couldn't be verified

This can indicate that a dependency has been compromised. Please carefully verify the signatures and checksums."""
        }
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }

        when: "refreshes the keys"
        succeeds ":compileJava", "--refresh-keys"

        then:
        noExceptionThrown()

        where:
        terse << [true, false]
    }


    def "cache takes ignored keys into consideration"() {
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
        }

        given:
        terseConsoleOutput(false)
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                signAsciiArmored(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        fails ":compileJava"

        then:
        failure.assertHasCause """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key '14f53f0824875d73' but it wasn't found in any key server so it couldn't be verified
  - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': Artifact was signed with key '14f53f0824875d73' but it wasn't found in any key server so it couldn't be verified

This can indicate that a dependency has been compromised. Please carefully verify the signatures and checksums."""
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }

        when: "ignore key"
        replaceMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addGloballyIgnoredKey("14f53f0824875d73")
        }
        fails ":compileJava"

        then:
        failure.assertHasCause """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': checksum is missing from verification metadata.
  - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': checksum is missing from verification metadata."""
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }

        when: "doesn't ignore key anymore"
        replaceMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
        }
        fails ":compileJava"

        then:
        failure.assertHasCause """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key '14f53f0824875d73' but it wasn't found in any key server so it couldn't be verified
  - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': Artifact was signed with key '14f53f0824875d73' but it wasn't found in any key server so it couldn't be verified
"""
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }

        when: "ignore key only for artifact"
        replaceMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addIgnoredKeyByFileName("org:foo:1.0", "foo-1.0.jar", "14f53f0824875d73")
        }
        fails ":compileJava"

        then:
        failure.assertHasCause """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': Artifact was signed with key '14f53f0824875d73' but it wasn't found in any key server so it couldn't be verified
  - On artifact foo-1.0.jar (org:foo:1.0) multiple problems reported:
      - in repository 'maven': artifact was signed but all keys were ignored
      - in repository 'maven': checksum is missing from verification metadata."""
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }
    }


    // This test exercises the fact that the signature cache is aware
    // of changes of the artifact
    @ToBeFixedForConfigurationCache
    def "can detect tampered file between builds (terse output=#terse)"() {
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKey("org:foo:1.0", SigningFixtures.validPublicKeyHexString)
            addTrustedKey("org:foo:1.0", SigningFixtures.validPublicKeyHexString, "pom", "pom")
        }
        keyServerFixture.withDefaultSigningKey()

        given:
        terseConsoleOutput(terse)
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                signAsciiArmored(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        succeeds ":compileJava"

        then:
        noExceptionThrown()

        when:
        def group = new File(CacheLayout.FILE_STORE.getPath(metadataCacheDir), "org")
        def module = new File(group, "foo")
        def version = new File(module, "1.0")
        def originHash = new File(version, "16e066e005a935ac60f06216115436ab97c5da02")
        def artifactFile = new File(originHash, "foo-1.0.jar")
        artifactFile.text = "tampered"
        fails ":compileJava"

        then:
        assertVerificationError(terse) {
            whenTerse """Dependency verification failed for configuration ':compileClasspath'
One artifact failed verification: foo-1.0.jar (org:foo:1.0) from repository maven
This can indicate that a dependency has been compromised. Please carefully verify the signatures and checksums."""

            whenVerbose """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key 'd7bf96a169f77b28c934ab1614f53f0824875d73' (Gradle Test (This is used for testing the gradle-signing-plugin) <test@gradle.org>) but signature didn't match"""
        }

        where:
        terse << [true, false]
    }

    @ToBeFixedForConfigurationCache
    def "caching takes trusted keys into account (terse output=#terse)"() {
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKey("org:foo:1.0", validPublicKeyHexString)
            addTrustedKey("org:foo:1.0", validPublicKeyHexString, "pom", "pom")
        }
        keyServerFixture.withDefaultSigningKey()

        given:
        terseConsoleOutput(terse)
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                signAsciiArmored(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        succeeds ":compileJava"

        then:
        noExceptionThrown()

        when:
        replaceMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKey("org:foo:1.0", SigningFixtures.validPublicKeyHexString, "pom", "pom")
        }
        fails ":compileJava"

        then:
        assertVerificationError(terse) {
            whenTerse """Dependency verification failed for configuration ':compileClasspath'
One artifact failed verification: foo-1.0.jar (org:foo:1.0) from repository maven
If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file by following the instructions at ${docsUrl}"""

            whenVerbose """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key 'd7bf96a169f77b28c934ab1614f53f0824875d73' (Gradle Test (This is used for testing the gradle-signing-plugin) <test@gradle.org>) and passed verification but the key isn't in your trusted keys list.

If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file by following the instructions at ${docsUrl}"""
        }

        where:
        terse << [true, false]
    }

    def "unsigned artifacts require checksum verification (terse output=#terse)"() {
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addChecksum("org:foo:1.0", "sha256", "nope")
            addChecksum("org:foo:1.0", "sha256", "nope", "pom", "pom")
        }

        given:
        terseConsoleOutput(terse)
        javaLibrary()
        uncheckedModule("org", "foo", "1.0")
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        fails ":compileJava"

        then:
        assertVerificationError(terse) {
            whenTerse """Dependency verification failed for configuration ':compileClasspath'
2 artifacts failed verification:
  - foo-1.0.jar (org:foo:1.0) from repository maven
  - foo-1.0.pom (org:foo:1.0) from repository maven
This can indicate that a dependency has been compromised. Please carefully verify the checksums."""

            whenVerbose """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.jar (org:foo:1.0) multiple problems reported:
      - in repository 'maven': artifact wasn't signed
      - in repository 'maven': expected a 'sha256' checksum of 'nope' but was '20ae575ede776e5e06ee6b168652d11ee23069e92de110fdec13fbeaa5cf3bbc'
  - On artifact foo-1.0.pom (org:foo:1.0) multiple problems reported:
      - in repository 'maven': artifact wasn't signed
      - in repository 'maven': expected a 'sha256' checksum of 'nope' but was 'f331cce36f6ce9ea387a2c8719fabaf67dc5a5862227ebaa13368ff84eb69481'

This can indicate that a dependency has been compromised. Please carefully verify the checksums."""
        }
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }

        where:
        terse << [true, false]
    }

    def "can ignore a key and fallback to checksum verification (terse output=#terse)"() {
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addGloballyIgnoredKey(getValidPublicKeyLongIdHexString())
        }

        given:
        terseConsoleOutput(terse)
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                signAsciiArmored(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        fails ":compileJava"

        then:
        assertVerificationError(terse) {
            whenTerse """Dependency verification failed for configuration ':compileClasspath'
2 artifacts failed verification:
  - foo-1.0.jar (org:foo:1.0) from repository maven
  - foo-1.0.pom (org:foo:1.0) from repository maven"""

            whenVerbose """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': checksum is missing from verification metadata.
  - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': checksum is missing from verification metadata."""
        }
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }

        where:
        terse << [true, false]
    }

    def "can ignore a key using full fingerprint and fallback to checksum verification (terse output=#terse)"() {
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addGloballyIgnoredKey(SigningFixtures.validPublicKeyHexString)
        }

        given:
        terseConsoleOutput(terse)
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                signAsciiArmored(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        serveValidKey()
        fails ":compileJava"

        then:
        assertVerificationError(terse) {
            whenTerse """Dependency verification failed for configuration ':compileClasspath'
2 artifacts failed verification:
  - foo-1.0.jar (org:foo:1.0) from repository maven
  - foo-1.0.pom (org:foo:1.0) from repository maven"""

            whenVerbose """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': checksum is missing from verification metadata.
  - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': checksum is missing from verification metadata."""
        }
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }

        where:
        terse << [true, false]
    }

    def "can ignore a key for a specific artifact and fallback to checksum verification (terse output=#terse)"() {
        // we tamper the jar, so the verification of the jar would fail, but not the POM
        keyServerFixture.withDefaultSigningKey()
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addIgnoredKeyByFileName("org:foo:1.0", "foo-1.0.jar", validPublicKeyHexString)
        }

        given:
        terseConsoleOutput(terse)
        javaLibrary()
        def module = uncheckedModule("org", "foo", "1.0") {
            withSignature {
                signAsciiArmored(it)
            }
        }
        tamperWithFile(module.artifactFile)

        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        fails ":compileJava"

        then:
        // jar file fails because it doesn't have any checksum declared, despite ignoring the key, which is what we want
        // and pom file fails because we didn't trust the key
        assertVerificationError(terse) {
            whenTerse """Dependency verification failed for configuration ':compileClasspath'
2 artifacts failed verification:
  - foo-1.0.pom (org:foo:1.0) from repository maven
  - foo-1.0.jar (org:foo:1.0) from repository maven"""

            whenVerbose """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': Artifact was signed with key 'd7bf96a169f77b28c934ab1614f53f0824875d73' (Gradle Test (This is used for testing the gradle-signing-plugin) <test@gradle.org>) and passed verification but the key isn't in your trusted keys list.
  - On artifact foo-1.0.jar (org:foo:1.0) multiple problems reported:
      - in repository 'maven': artifact was signed but all keys were ignored
      - in repository 'maven': checksum is missing from verification metadata.

This can indicate that a dependency has been compromised. Please carefully verify the signatures and checksums."""
        }
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }

        where:
        terse << [true, false]

    }

    def "can ignore a key by long id for a specific artifact and fallback to checksum verification (terse output=#terse)"() {
        // we tamper the jar, so the verification of the jar would fail, but not the POM
        keyServerFixture.withDefaultSigningKey()
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addIgnoredKeyByFileName("org:foo:1.0", "foo-1.0.jar", validPublicKeyLongIdHexString)
        }

        given:
        terseConsoleOutput(terse)
        javaLibrary()
        def module = uncheckedModule("org", "foo", "1.0") {
            withSignature {
                signAsciiArmored(it)
            }
        }
        tamperWithFile(module.artifactFile)

        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        fails ":compileJava"

        then:
        // jar file fails because it doesn't have any checksum declared, despite ignoring the key, which is what we want
        // and pom file fails because we didn't trust the key
        assertVerificationError(terse) {
            whenTerse """Dependency verification failed for configuration ':compileClasspath'
2 artifacts failed verification:
  - foo-1.0.pom (org:foo:1.0) from repository maven
  - foo-1.0.jar (org:foo:1.0) from repository maven"""

            whenVerbose """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': Artifact was signed with key 'd7bf96a169f77b28c934ab1614f53f0824875d73' (Gradle Test (This is used for testing the gradle-signing-plugin) <test@gradle.org>) and passed verification but the key isn't in your trusted keys list.
  - On artifact foo-1.0.jar (org:foo:1.0) multiple problems reported:
      - in repository 'maven': artifact was signed but all keys were ignored
      - in repository 'maven': checksum is missing from verification metadata.

This can indicate that a dependency has been compromised. Please carefully verify the signatures and checksums."""
        }
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }

        where:
        terse << [true, false]

    }

    def "passes verification if an artifact is signed with multiple keys and one of them is ignored"() {
        def keyring = newKeyRing()
        keyServerFixture.registerPublicKey(keyring.publicKey)
        def pkId = toLongIdHexString(keyring.publicKey.keyID)
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            // only the new keyring key is published and available
            addGloballyIgnoredKey(validPublicKeyLongIdHexString)
            addTrustedKey("org:foo:1.0", pkId)
            addTrustedKey("org:foo:1.0", pkId, "pom", "pom")
        }

        given:
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                keyring.sign(it, [(SigningFixtures.validSecretKey): SigningFixtures.validPassword])
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        succeeds ":compileJava"

        then:
        noExceptionThrown()
    }

    def "can collect multiple errors for single dependency (terse output=#terse)"() {
        def keyring = newKeyRing()
        keyServerFixture.registerPublicKey(keyring.publicKey)
        def pkId = Fingerprint.of(keyring.publicKey)

        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKey("org:foo:1.0", validPublicKeyHexString)
            addTrustedKey("org:foo:1.0", validPublicKeyHexString, "pom", "pom")
        }

        given:
        terseConsoleOutput(terse)
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                keyring.sign(it, [(SigningFixtures.validSecretKey): SigningFixtures.validPassword])
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        fails ":compileJava"

        then:
        assertVerificationError(terse) {
            whenTerse """Dependency verification failed for configuration ':compileClasspath'
2 artifacts failed verification:
  - foo-1.0.jar (org:foo:1.0) from repository maven
  - foo-1.0.pom (org:foo:1.0) from repository maven"""

            whenVerbose """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': Multiple signature verification errors found:
      - Artifact was signed with key '14f53f0824875d73' but it wasn't found in any key server so it couldn't be verified
      - Artifact was signed with key '${pkId}' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.
  - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': Multiple signature verification errors found:
      - Artifact was signed with key '14f53f0824875d73' but it wasn't found in any key server so it couldn't be verified
      - Artifact was signed with key '${pkId}' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.

If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file by following the instructions at ${docsUrl}"""
        }
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }

        where:
        terse << [true, false]
    }

    def "can declare globally trusted keys"() {
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addGloballyTrustedKey(validPublicKeyHexString, "org")
        }

        given:
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                signAsciiArmored(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        serveValidKey()

        then:
        succeeds ":compileJava"

    }

    def "can mix globally trusted keys and artifact specific keys (trust artifact key = #addLocalKey)"() {
        def keyring = newKeyRing()
        keyServerFixture.registerPublicKey(keyring.publicKey)
        def pkId = Fingerprint.of(keyring.publicKey)

        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addGloballyTrustedKey(validPublicKeyHexString, "o.*", "foo", null, null, true)
            if (addLocalKey) {
                addTrustedKey("org:foo:1.0", pkId.toString())
                addTrustedKey("org:foo:1.0", pkId.toString(), "pom", "pom")
            }
        }

        given:
        terseConsoleOutput(false)
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                keyring.sign(it, [(SigningFixtures.validSecretKey): SigningFixtures.validPassword])
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        serveValidKey()
        if (addLocalKey) {
            succeeds ":compileJava"
        } else {
            fails ":compileJava"
        }

        then:
        if (!addLocalKey) {
            failure.assertHasCause """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key '${pkId}' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.
  - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': Artifact was signed with key '${pkId}' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.

If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file by following the instructions at ${docsUrl}"""
            if (GradleContextualExecuter.isConfigCache()) {
                failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
            }
        }

        where:
        addLocalKey << [true, false]
    }

    def "can read public keys from #keyRingFormat keyring"() {
        // key will not be published on the server fixture but available locally
        def keyring = newKeyRing()
        def pkId = toLongIdHexString(keyring.publicKey.keyID)

        createMetadataFile {
            disableKeyServers()
            verifySignatures()
            addTrustedKey("org:foo:1.0", pkId)
            addTrustedKey("org:foo:1.0", pkId, "pom", "pom")
        }

        def verifFile = file("gradle/verification-keyring.${extension}")
        keyring.writePublicKeyRingTo(verifFile)
        if (header != null) {
            verifFile.setText("""$header
${verifFile.getText('us-ascii')}""", 'us-ascii')
        }

        given:
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                keyring.sign(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        expect:
        succeeds ":compileJava"

        where:
        keyRingFormat       | extension | header
        'GPG'               | 'gpg'     | null
        'ASCII'             | 'keys'    | null
        'ASCII with header' | 'keys'    | 'some comment showing we can have arbitrary text'
    }

    @UnsupportedWithConfigurationCache
    def "can verify dependencies in a buildFinished hook (terse output=#terse)"() {
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKey("org:foo:1.0", validPublicKeyHexString, "pom", "pom")
        }

        given:
        terseConsoleOutput(terse)
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                signAsciiArmored(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }

            gradle.buildFinished {
               allprojects {
                  println configurations.compileClasspath.files
               }
            }
        """

        when:
        serveValidKey()
        fails ":help"

        then:
        failure.assertHasDescription terse ? """Dependency verification failed for configuration ':compileClasspath'
One artifact failed verification: foo-1.0.jar (org:foo:1.0) from repository maven""" : """Dependency verification failed for configuration ':compileClasspath':
  - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key 'd7bf96a169f77b28c934ab1614f53f0824875d73' (Gradle Test (This is used for testing the gradle-signing-plugin) <test@gradle.org>) and passed verification but the key isn't in your trusted keys list.
"""
        where:
        terse << [true, false]
    }

    def "dependency verification should work correctly with artifact queries"() {
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKey("org:foo:1.0", validPublicKeyHexString, "pom", "pom")
            addTrustedKey("org:foo:1.0", validPublicKeyHexString, "jar", "jar")
            addTrustedKeyByFileName("org:foo:1.0", "foo-1.0-javadoc.jar", validPublicKeyHexString)
            addTrustedKeyByFileName("org:foo:1.0", "foo-1.0-sources.jar", validPublicKeyHexString)
        }

        terseConsoleOutput(false)
        javaLibrary()
        def module = mavenHttpRepo.module("org", "foo", "1.0")
            .withSourceAndJavadoc()
            .publish()

        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }

            tasks.register("artifactQuery") {
                doLast {
                    dependencies.createArtifactResolutionQuery()
                        .forModule("org", "foo", "1.0")
                        .withArtifacts(JvmLibrary, SourcesArtifact, JavadocArtifact)
                        .execute()
                        .components
                        .each {
                            // trigger file access for verification to happen
                            it.getArtifacts(SourcesArtifact)*.file
                            it.getArtifacts(JavadocArtifact)*.file
                        }
                }
            }
        """

        when:
        module.pom.expectGet()
        module.getArtifact(type:'pom.asc').expectGet()
        module.getArtifact(classifier:'sources').expectHead()
        module.getArtifact(classifier:'sources').expectGet()
        module.getArtifact(classifier:'sources', type:'jar.asc').expectGet()
        module.getArtifact(classifier:'javadoc').expectHead()
        module.getArtifact(classifier:'javadoc').expectGet()
        module.getArtifact(classifier:'javadoc', type:'jar.asc').expectGet()
        run ":artifactQuery"

        then:
        noExceptionThrown()
    }

    def "can disable reaching out to key servers"() {
        createMetadataFile {
            keyServer(keyServerFixture.uri) // make sure we declare a key server for tests
            disableKeyServers() // but disable access to the key server
            verifySignatures()
            addTrustedKey("org:foo:1.0", validPublicKeyHexString)
            addTrustedKey("org:foo:1.0", validPublicKeyHexString, "pom", "pom")
        }
        serveValidKey()

        given:
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                signAsciiArmored(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        fails ":compileJava"

        then:
        failure.assertHasCause """Dependency verification failed for configuration ':compileClasspath'
2 artifacts failed verification:
  - foo-1.0.jar (org:foo:1.0) from repository maven
  - foo-1.0.pom (org:foo:1.0) from repository maven
This can indicate that a dependency has been compromised. Please carefully verify the signatures and checksums. Key servers are disabled, this can indicate that you need to update the local keyring with the missing keys."""
        if (GradleContextualExecuter.isConfigCache()) {
            failure.assertThatDescription(containsText("""Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: value 'configuration ':compileClasspath'' failed to visit file collection"""))
        }
    }

    @ToBeFixedForConfigurationCache
    @Issue("https://github.com/gradle/gradle/issues/19663")
    def "fails when disabling reaching out to key servers after previous successful build and no key rings file"() {
        given:
        createMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKey("org:foo:1.0", validPublicKeyHexString)
            addTrustedKey("org:foo:1.0", validPublicKeyHexString, "pom", "pom")
        }
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                signAsciiArmored(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """
        serveValidKey()

        when:
        succeeds ":compileJava"

        then:
        noExceptionThrown()

        when:
        replaceMetadataFile {
            keyServer(keyServerFixture.uri)
            verifySignatures()
            addTrustedKey("org:foo:1.0", validPublicKeyHexString)
            addTrustedKey("org:foo:1.0", validPublicKeyHexString, "pom", "pom")
            disableKeyServers()
        }
        fails ":compileJava"

        then:
        failure.assertHasCause """Dependency verification failed for configuration ':compileClasspath'
2 artifacts failed verification:
  - foo-1.0.jar (org:foo:1.0) from repository maven
  - foo-1.0.pom (org:foo:1.0) from repository maven
This can indicate that a dependency has been compromised. Please carefully verify the signatures and checksums. Key servers are disabled, this can indicate that you need to update the local keyring with the missing keys."""
    }

    @ToBeFixedForConfigurationCache
    @Issue("https://github.com/gradle/gradle/issues/18440")
    def "fails on a bad verification file change after previous successful build when key servers are disabled"() {
        def keyring = newKeyRing()
        def pkId = toLongIdHexString(keyring.publicKey.keyID)

        createMetadataFile {
            disableKeyServers()
            verifySignatures()
            addTrustedKey("org:foo:1.0", pkId)
            addTrustedKey("org:foo:1.0", pkId, "pom", "pom")
        }

        def verificationFile = file("gradle/verification-keyring.keys")
        keyring.writePublicKeyRingTo(verificationFile)

        given:
        javaLibrary()
        uncheckedModule("org", "foo", "1.0") {
            withSignature {
                keyring.sign(it)
            }
        }
        buildFile << """
            dependencies {
                implementation "org:foo:1.0"
            }
        """

        when:
        succeeds ":compileJava"

        then:
        noExceptionThrown()

        when:
        verificationFile.delete()
        fails ":compileJava"

        then:
        failure.assertHasCause """Dependency verification failed for configuration ':compileClasspath'
2 artifacts failed verification:
  - foo-1.0.jar (org:foo:1.0) from repository maven
  - foo-1.0.pom (org:foo:1.0) from repository maven
This can indicate that a dependency has been compromised. Please carefully verify the signatures and checksums. Key servers are disabled, this can indicate that you need to update the local keyring with the missing keys."""
    }

    private static void tamperWithFile(File file) {
        file.bytes = [0, 1, 2, 3] + file.readBytes().toList() as byte[]
    }
}
