Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 43 additions & 46 deletions src/site/markdown/about-checksums.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,65 +18,62 @@ specific language governing permissions and limitations
under the License.
-->

Maven Resolver uses checksums to verify the integrity of downloaded artifacts and
metadata. Checksums are usually placed in repositories next to the file in question, with the file
extension indicating the checksum algorithm that produced the given file. Currently,
most Maven repositories contain SHA-1 and MD5 checksums as they are produced by Resolver by default.

Historically, Maven Resolver used `java.security.MessageDigest` to implement checksums. Secure one-way
hashes provided by the Java Cryptography Architecture were (mis)used to implement checksums for transport integrity
validation. Secure hashes MAY be used as checksums, as there is quite some
overlap between checksums and hashes in general. But this simplicity comes at a price: cryptographically safe
algorithms require way more CPU cycles to compute than a simple checksum. However, the purpose of a checksum is just
integrity validation, nothing more. There is no security or trust implied or expected from
them. Checksums do not protect against man-in-the-middle or supply chain attacks.

To actually trust that artifacts have not been tampered with, you need signatures such as
Maven Resolver uses checksums to verify the integrity of downloaded artifacts and metadata.
Checksums exist in repositories next to the target file.
The file extension identifies the checksum algorithm that produced the checksum.
Most Maven repositories contain SHA-1 and MD5 checksums by default.
Maven Resolver also produces these checksums by default.
Checksums only provide integrity verification. They do not provide security or trust.
They do not protect against man-in-the-middle or supply chain attacks.

In the past, Maven Resolver used `java.security.MessageDigest` to calculate checksums.
The Java Cryptography Architecture provides secure one-way hashes.
Maven Resolver used these secure hashes to verify transport integrity.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original's "(mis)used" was intentional editorial commentary — it acknowledged that applying cryptographic hash functions as transport checksums was a design shortcut, which motivates the SPI section that follows. Dropping it entirely loses that context. Consider preserving the nuance, e.g.:

Maven Resolver repurposed these secure hashes as checksums for transport integrity validation.

Secure hashes work as checksums, but cryptographically safe algorithms
require many more CPU cycles to calculate than a simple checksum.

Some users state that specific algorithms are unsafe or deprecated.
This argument does not apply to Maven Resolver because checksums do not provide security.
This fact is true for the SHA-1 algorithm and the MD5 algorithm.
Industry still uses both algorithms today to verify transport integrity and to detect errors.

To prove that artifacts have not been tampered with, you need signatures such as

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: "prove" is stronger than the original "trust." GPG signatures provide cryptographic assurance from a trusted signer — they establish trust, not mathematical proof. The original word was more precise.

Suggested change
To prove that artifacts have not been tampered with, you need signatures such as
To trust that artifacts have not been tampered with, you need signatures such as

those provided by the
[Maven GPG Plugin](https://maven.apache.org/plugins/maven-gpg-plugin/).

Hence, the usual argument that "XXX algorithm is unsafe, deprecated, not secure anymore" does not apply in the case
of Maven Resolver. Moreover, this is true not only for SHA-1
algorithm, but even for its "elder brother" MD5. A checksum is not intended to be secure. Both algorithms are still widely used today as "transport integrity
validation" or "error detection" (a.k.a. "bit-rot detection").
## Checksum Algorithms Service Provider Interface (SPI)

## Checksum Algorithms SPI
System properties enable users to specify arbitrary checksum algorithms,
even if they are not part of the standard Maven process.
Users can also register an alternate provider for Java Cryptography that
supplies a broader set of message digests for checksums.
The Maven Resolver team discourages this.

From a technical perspective, the above facts imply the following consequences: because checksum algorithms are exposed
to the user, one can set them via configuration, and thus users are not prevented from asking for SHA-256 or even SHA-512, even if
these algorithms are not part of standard Maven process. Moreover, nothing prevents users (integrating
Maven Resolver) registering an alternate Java Cryptography Provider and using even broader (or exotic)
message digest algorithms for checksums. While this is not wrong, we do consider this as a
bad use case. The notion of transport validation and secure hashes are being constantly mixed up due to historical
reasons explained above.

Hence, the Maven Resolver team decided to make the supported set of checksum algorithms more controlled. Instead of directly exposing
`MessageDigest` algorithms, we introduced an SPI around checksums. This not only prevents incorrect use cases by not
exposing all supported algorithms of `MessageDigest` to users, but also makes it possible to introduce real checksum
algorithms. Finally, the set of supported checksum algorithms remains extensible: if some required algorithm is
not provided by Resolver, it can easily be added by creating a factory component for it.

We are aware that users started using "better SHA" algorithms, and we do not want to break them. Nothing for them
changes (configuration and everything basically remains the same). But we do want to prevent any possible further
proliferation of non-standard checksums.

## Implemented Checksum Algorithms

Resolver out of the box provides the following checksum algorithms (important: algorithm names are case sensitive):
To control the supported set of checksums, the Maven Resolver team introduced an SPI for checksums.
We no longer expose `MessageDigest` algorithms directly.
Instead, the SPI supports four checksum algorithms:

* MD5
* SHA-1
* SHA-256
* SHA-512

The algorithms above are provided by Resolver, by default. Still, using the SPI, anyone can extend
Resolver with new types of Checksum Algorithms.

To see how and when checksums are used in Resolver, continue on [Expected Checksums](expected-checksums.html)
page.
The names of these algorithms are case-sensitive.

You can use the SPI to extend Maven Resolver with other checksum algorithms.
If Maven Resolver does not provide a required algorithm, you can create a
factory component for the new algorithm to add it.

We know that users use stronger SHA algorithms.
We do not want to break these configurations.
Configuration and operations remain the same for these users.
However, we want to prevent the future addition of non-standard checksums.

The [Expected Checksums](expected-checksums.html) page explains how and when Maven Resolver uses checksums.

Links:

* [SHA-1](https://en.wikipedia.org/wiki/SHA-1) (see "Data Integrity" section)
* [MD5](https://en.wikipedia.org/wiki/MD5) (see "Applications" section, especially about error checking functionality)
* [SHA-1](https://en.wikipedia.org/wiki/SHA-1) (The "Data Integrity" section explains this concept.)
* [MD5](https://en.wikipedia.org/wiki/MD5) (The "Applications" section explains the error verification function.)

118 changes: 54 additions & 64 deletions src/site/markdown/api-compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,93 +19,83 @@ specific language governing permissions and limitations
under the License.
-->

Maven Resolver exposes three modules for clients and those extending Maven Resolver:
* maven-resolver-api (in short API) -- for clients and those extending it
* maven-resolver-spi (in short SPI) -- for those extending it
* maven-resolver-util (in short Util) -- for client and those extending it
Maven Resolver exposes three modules for client applications and extensions.
Client applications invoke methods in these modules.
Extensions inherit from classes and implement interfaces.

Each module guarantees non-breaking (source and binary) compatibility, as long
clients and extenders obey some rules. If you break any of these rules, you are
prone to breakage, and you are on your own.
* `maven-resolver-api` (API) - Client applications and extensions use this module.
* `maven-resolver-spi` (SPI) - Extensions use this module.
* `maven-resolver-util` (Util) - Client applications and extensions use this module.

## Interface And (Abstract) Class Level Contracts
If you obey specific rules, these modules will be source and binary compatible across minor releases.
If you break these rules, your code can break when you update these modules.

In source, we use two important Javadoc tags to mark intent:
* `@noextend` -- classes (or interfaces) carrying this tag MUST NOT be extended
* `@noimplement` -- interfaces carrying this tag MUST NOT be directly or indirectly implemented,
UNLESS the Javadoc of given interface points to an abstract support class that makes indirect
implementation possible.
## Interface And (Abstract) Class Level Contracts

Examples:
We use two Javadoc tags in the source code to mark intent:
* `@noextend` - You must not extend classes or interfaces with this tag.
* `@noimplement` - You must not implement interfaces with this tag directly or indirectly.

* `RepositorySystem` interface. It carries both `@noextend` and `@noimplement` tags. This interface
MUST NOT be extended nor implemented. This is a component interface, that is usually injected into
client application.
* `TransferListener` interface. It carries both `@noextend` and `@noimplement` tags, but Javadoc
points at `AbstractTransferListener` as extension point. Hence, clients are NOT allowed to extend
this interface, nor to directly implement it, but, if custom listener is needed, it is warmly
advised to extend the given abstract class. This way we can protect you from future breakage.
If the Javadoc points to an abstract support class, you can implement the `@noimplement` interface indirectly.

## Package Level Contracts
Examples:

Maven Resolver implements customary habit to name packages NOT meant to be accessed by clients.
If a Java package contains following names:
The `RepositorySystem` interface has the `@noextend` tag and the `@noimplement` tag.
You must not extend or implement this interface.
The `RepositorySystem` interface is a component interface.
Client applications usually receive this interface through dependency injection.

* `impl`
* `internal`
The `TransferListener` interface has the `@noextend` tag and the `@noimplement` tag.
The Javadoc points to the `AbstractTransferListener` abstract class.
You must not extend or implement the `TransferListener` interface directly.
If you need a custom listener, extend the `AbstractTransferListener` abstract class.
This abstract class protects your code from future breakages.

That Java package is meant as "internal" and does NOT offer guarantees of compatibility as API is. You
may use classes from these packages, but again, you are on your own to deal with (binary or source)
breakages. If you think a class from such package should be "pulled out" and made part of SPI or
maybe API, better inform us via [JIRA](https://issues.apache.org/jira/projects/MRESOLVER): create a
ticket and let's discuss.
## Package Level Contracts

As a side note, the count of those names in Java package is directly proportional to possibility of
breaking changes: the more, the larger the possibility of breakage even in minor releases.
Maven Resolver identifies internal Java packages with the words `impl` and `internal`.
These internal packages do not guarantee compatibility between releases.
If you use classes from these packages, you must fix source breakages and binary breakages yourself.
You can request to move a class to the API or the SPI through a ticket on [GitHub](https://github.com/apache/maven-resolver/issues).

## Version Level Contracts

Maven Resolver does NOT use "semantic versioning", but still tries at best to reflect contained
changes using version number. We use "major.minor.patch" versioning on resolver with following
semantics:

* On major version change, one should NOT expect any backward compatibility.
* On minor version change, we ENSURE backward compatibility for those "exposed" 3 modules: API,
SPI and Util. Still, there are examples when we failed to do so, usually driven by new
features.
Maven Resolver does not use "semantic versioning".
However, Maven Resolver uses a "major.minor.patch" version format to indicate changes.
Major version changes do not provide backward compatibility.
The API, SPI, and Util modules should be backwards compatible across minor version changes.
However, we have violated this rule in the past, usually to support new features.

In any of three version changes above, in areas where we do not offer guarantees, everything
can happen.
Maven Resolver does not guarantee compatibility for internal modules.
Internal modules can change in any version update.

## Outside of Maven

Applications integrating Maven Resolver outside of Maven has really simple job: all they have to
ensure is that API, SPI, Util and the rest of resolver (impl, basic-connector and transports)
have all same versions, and they can rely on these backward compatibility contracts as explained
above.
Applications can use Maven Resolver outside of Maven.
These applications must use the same version for all Maven Resolver modules.
For example, the API, SPI, Util, `impl`, `basic-connector`, and transports must share the same version.
If the versions match, the applications can rely on the compatibility guarantees.

## Inside of Maven

Historically, Maven 3.1 provided API, SPI
and Impl from its own embedded resolver, while Util and Connector, if some plugin or extension
depended on them, were resolved separately. This meant that a plugin could work with different versions
of API, SPI, Impl or Connector. Because the Resolver API was "frozen" for too long a time, this was essentially
not a problem, but still weird.
In the past, Maven 3.1 provided the API, SPI, and `impl` modules from an embedded resolver.
Plugins resolved the Util and Connector modules separately.
Therefore, plugins used different versions of these modules.
The static API prevented major problems.

This changes in Maven 3.9+: Maven starting with version 3.9.0 will provide API, SPI, Impl,
**and Util and Connector**. Reason for this change is that Impl and Connector bundled in Maven
implement things from both API and SPI, and there was a binary incompatible change between
Resolver 1.8.0 and previous versions.
Maven 3.9.0 provides the API, SPI, `impl`, Util, and Connector modules.
The bundled `impl` and Connector modules implement the API and the SPI.
A binary incompatibility occurred between Maven Resolver 1.8.0 and previous versions.
Because of this incompatibility, Maven 3.9.0 bundles all modules to ensure stability.

Most Resolver users should not be affected by this change.
This change does not affect most Maven Resolver users.

The binary incompatible change happened in the SPI class `RepositoryLayout` as part of work done for
[MRESOLVER-230](https://issues.apache.org/jira/browse/MRESOLVER-230), and affects both, Connector
and Impl.
The binary incompatibility occurred in the `RepositoryLayout` SPI class for [MRESOLVER-230](https://issues.apache.org/jira/browse/MRESOLVER-230).
This incompatibility affects the Connector module and the `impl` module.

## Backward Compatibility Checks

To ensure backward compatibility, starting from 1.9.0 Maven Resolver uses
[JApiCmp](https://siom79.github.io/japicmp/MavenPlugin.html),
with two executions (for source and binary level checks). The plugin is enabled on 3 modules of
Resolver mentioned at page top: API, SPI and Util. For "baseline" we use version 1.8.0.
Maven Resolver uses [JApiCmp](https://siom79.github.io/japicmp/MavenPlugin.html) to verify backward compatibility.
Starting with version 1.9.0, Maven Resolver runs this plugin twice to verify source compatibility and binary compatibility.
The plugin runs on the API, SPI, and Util modules.
The compatibility baseline is version 1.8.0.
Loading