Skip to content

Commit e4bc7e3

Browse files
joewizclaude
andcommitted
[feature] v7.0.0: Complete rewrite as drop-in replacement
Complete rewrite of the EXPath Cryptographic Module for eXist-db 7.0+. Zero external dependencies — uses only Java's built-in JCE. Drop-in replacement: same package URI (http://expath.org/ns/crypto), same module namespace, same function names, all v6 arities preserved. Targets conformance with four spec sources: - EXPath Crypto 1.0 (2010): original specification - EXPath Crypto Editor's Draft (2017): map-based parameters - EXPath Crypto 2018 CG Final Report: list-* introspection, DSAwithSHA1/RSAwithSHA1 algorithm names, Canonicalization 1.1, map-based generate-signature - BaseX Cryptographic Functions: cross-engine portability What's new: - Binary input support for crypto:hmac (xs:base64Binary, xs:hexBinary) - All generate-signature arities: 6, 7 (xpath), 8 (cert), 3 (key), 2 (map-based per 2018 CG / editor's draft) - 6-param encrypt/decrypt for backward compatibility (iv + provider) - Algorithm name aliases: DSAwithSHA1/RSAwithSHA1 (2018 CG spec) - Canonicalization 1.1: inclusive-1.1, inclusive-with-comments-1.1 - BaseX-compatible signatures (lowercase algorithm names) - RSA_SHA256 signature algorithm; AES-192 and AES-256 key sizes - list-providers/services/algorithms implemented (2018 CG spec), disabled pending eXist binary search fix for zero-param arities - 102 tests (64 JUnit + 38 XQSuite) including BaseX cross-compat vectors - CI: Java 21 on 3 OSes, dependabot for Maven + Actions Breaking changes from v6: - crypto:encrypt output is xs:base64Binary (was dash-separated bytes) - DSA_SHA1 signatures forbidden by Java 21 secure validation Requires eXist-db 6.2+ and Java 21+. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 23ef80e commit e4bc7e3

36 files changed

Lines changed: 3227 additions & 3123 deletions

.github/dependabot.yml

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
version: 2
22
updates:
3-
- package-ecosystem: maven
4-
directory: "/"
5-
schedule:
6-
interval: daily
7-
time: "03:00"
8-
open-pull-requests-limit: 10
9-
ignore:
10-
- dependency-name: org.apache.logging.log4j:log4j-api
11-
versions:
12-
- 2.14.0
3+
- package-ecosystem: maven
4+
directory: /
5+
schedule:
6+
interval: weekly
7+
- package-ecosystem: github-actions
8+
directory: /
9+
schedule:
10+
interval: weekly

.github/workflows/ci.yml

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,33 @@ jobs:
1313
fail-fast: false
1414
matrix:
1515
os: [ubuntu-latest, macos-latest, windows-latest]
16-
jdk: [1.8, 9, 11, 15]
16+
jdk: [21]
1717
runs-on: ${{ matrix.os }}
1818
steps:
19-
- uses: actions/checkout@v2
20-
- name: Set up JDK
21-
uses: actions/setup-java@v1
19+
- uses: actions/checkout@v4
20+
21+
- name: Set up JDK ${{ matrix.jdk }}
22+
uses: actions/setup-java@v4
2223
with:
2324
java-version: ${{ matrix.jdk }}
25+
distribution: zulu
26+
2427
- name: Cache Maven packages
25-
uses: actions/cache@v2
28+
uses: actions/cache@v4
2629
with:
27-
path: ~/.m2
30+
path: ~/.m2/repository
2831
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
2932
restore-keys: ${{ runner.os }}-m2
30-
- name: Maven Build
31-
run: mvn -V -B -DskipTests=true install
32-
- name: Maven Test
33-
run: mvn -B verify
34-
# - name: Maven Code Coverage
35-
# if: ${{ github.ref == 'refs/heads/main' && matrix.jdk == '1.8' && matrix.os == 'ubuntu-latest' }}
36-
# run: mvn -B jacoco:report coveralls:report -DrepoToken=${{ secrets.COVERALLS_TOKEN }}
33+
34+
- name: Clone and build eXist-db (SNAPSHOT dependency)
35+
shell: bash
36+
run: |
37+
git clone --depth 1 https://github.com/eXist-db/exist.git "$RUNNER_TEMP/exist-db"
38+
cd "$RUNNER_TEMP/exist-db"
39+
mvn -V -B -DskipTests -Ddependency-check.skip=true -Ddocker=false -pl exist-core,exist-start -am install
40+
41+
- name: Build
42+
run: mvn -V -B -DskipTests install
43+
44+
- name: Test
45+
run: mvn -B verify -Pintegration-tests

.gitignore

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
target/
2-
.DS_Store
32
*.iml
43
.idea/
5-
.classpath
4+
.settings/
65
.project
7-
.settings
8-
.vscode
9-
6+
.classpath
7+
*.swp
8+
*~

LGPL2.1-template.txt

Lines changed: 0 additions & 17 deletions
This file was deleted.

README.md

Lines changed: 158 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,177 @@
11
[![CI](https://github.com/eXist-db/expath-crypto-module/workflows/CI/badge.svg)](https://github.com/eXist-db/expath-crypto-module/actions?query=workflow%3ACI)
22

3-
# eXist-db implementation for EXPath Cryptographic Module
3+
# EXPath Crypto Module for eXist-db
44

5-
This is an eXist-db implementation of the [EXPath HTTP Crypto Module specification](http://expath.org/spec/crypto).
6-
## Building from source
5+
Version 7.0 — a drop-in replacement for [expath-crypto-module](https://github.com/eXist-db/expath-crypto-module) 6.x.
76

8-
Requires:
9-
* Java 1.8 or newer
10-
* Maven 3.6 or newer
7+
Implements the [EXPath Cryptographic Module](https://expath.org/spec/crypto) specification for eXist-db using Java's built-in JCE — no external dependencies. Function signatures are compatible with both the old eXist module and [BaseX's crypto module](https://docs.basex.org/main/Cryptographic_Functions) for cross-engine portability.
8+
9+
## Requirements
10+
11+
- **eXist-db 7.0+** (uses `exist.xml` auto-registration)
12+
- **Java 21+** (uses `HexFormat`, switch expressions, pattern matching)
13+
14+
Users on eXist 6 / Java 8–11 should continue using [expath-crypto-module 6.x](https://github.com/eXist-db/expath-crypto-module).
15+
16+
## Install
1117

1218
```bash
13-
$ git clone https://github.com/eXist-db/expath-crypto-module.git
14-
$ cd expath-crypto-module
15-
$ mvn clean package
19+
xst package install target/exist-crypto-7.0.0-SNAPSHOT.xar
20+
```
21+
22+
The `pre-install.xq` script automatically removes the old expath-crypto-module if present. The two packages share the same package URI (`http://expath.org/ns/crypto`) and cannot coexist.
23+
24+
## Functions
25+
26+
**Module namespace:** `http://expath.org/ns/crypto`
27+
28+
| Function | Arities | Spec | BaseX | v6 compat |
29+
|----------|---------|------|-------|-----------|
30+
| `crypto:hash` | 2, 3 | ✓ §2 | — (use `fn:hash`) ||
31+
| `crypto:hmac` | 3, 4 | ✓ §3 |||
32+
| `crypto:encrypt` | 4, 6 | ✓ §5 | ✓ (4-param) ||
33+
| `crypto:decrypt` | 4, 6 | ✓ §6 | ✓ (4-param) ||
34+
| `crypto:generate-signature` | 3, 6, 7, 8 | ✓ §7 | ✓ (6+2 optional) ||
35+
| `crypto:validate-signature` | 1 | ✓ §8 |||
36+
37+
### Hash
38+
39+
```xquery
40+
import module namespace crypto = "http://expath.org/ns/crypto";
41+
42+
crypto:hash("data", "SHA-256") (: Base64 output :)
43+
crypto:hash("data", "SHA-256", "hex") (: Hex output :)
44+
crypto:hash(<node>data</node>, "SHA-256") (: Node input :)
45+
```
46+
47+
Algorithms: MD5, SHA-1, SHA-256, SHA-384, SHA-512. For new code, prefer `fn:hash()` (XQuery 4.0) or `util:hash()`.
48+
49+
### HMAC
50+
51+
```xquery
52+
crypto:hmac("data", "secret-key", "SHA256") (: Base64 :)
53+
crypto:hmac("data", "secret-key", "SHA256", "hex") (: Hex :)
54+
crypto:hmac(xs:base64Binary("..."), "key", "SHA256") (: Binary input :)
55+
```
56+
57+
Algorithms: MD5, SHA1, SHA256, SHA384, SHA512. Names accept hyphenated (`SHA-256`), bare (`SHA256`), prefixed (`HMAC-SHA-256`), and lowercase (`sha256`) forms for BaseX compatibility.
58+
59+
### Symmetric Encryption
60+
61+
```xquery
62+
(: 4-param: auto-generated IV, prepended to ciphertext :)
63+
let $enc := crypto:encrypt("secret", "symmetric", $key, "AES")
64+
return crypto:decrypt($enc, "symmetric", $key, "AES")
65+
66+
(: 6-param: explicit IV and provider (v6 backward compat) :)
67+
let $enc := crypto:encrypt("secret", "symmetric", $key, "AES", $iv, $provider)
68+
return crypto:decrypt($enc, "symmetric", $key, "AES", $iv, $provider)
69+
```
70+
71+
Algorithms: AES (16/24/32-byte key), DES (8-byte key).
72+
73+
### XML Digital Signatures
74+
75+
```xquery
76+
(: 6-param: generated key pair :)
77+
let $signed := crypto:generate-signature(
78+
$doc, "exclusive", "SHA256", "RSA_SHA256", "dsig", "enveloped")
79+
return crypto:validate-signature($signed)
80+
81+
(: 7-param: + XPath expression for selective signing :)
82+
crypto:generate-signature($doc, "exclusive", "SHA256", "RSA_SHA256",
83+
"dsig", "enveloped", $xpath)
84+
85+
(: 8-param: + digital certificate from keystore :)
86+
crypto:generate-signature($doc, "exclusive", "SHA256", "RSA_SHA256",
87+
"dsig", "enveloped", $xpath, $certificate)
88+
89+
(: 3-param: simplified signing with PKCS#8 private key :)
90+
crypto:generate-signature($doc, $base64-private-key, "RSA_SHA256")
1691
```
1792

18-
This will create a "expath-crypto-module-<version>.xar" file in the target folder. The .xar file can be uploaded to any eXist-db version > 5.3.0 via the Dashboard.
19-
20-
### Currently implemented functions
93+
## Upgrading from expath-crypto-module 6.x
94+
95+
### No changes needed
96+
97+
The module namespace is identical — **import statements require no changes**:
98+
99+
```xquery
100+
import module namespace crypto = "http://expath.org/ns/crypto";
101+
```
102+
103+
All function names, arities, and algorithm names are backward compatible. Code using `crypto:hash`, `crypto:hmac`, `crypto:generate-signature` (6/7/8-param), `crypto:validate-signature`, and `crypto:encrypt`/`crypto:decrypt` (4-param and 6-param) will work without modification.
104+
105+
### Breaking changes
21106

22-
* crypto:hash()
23-
* crypto:hmac() (only for xs:string data for now)
24-
* crypto:encrypt() (only for xs:string data and symmetric encryption for now)
25-
* crypto:decrypt() (only for xs:string data and symmetric decryption for now)
26-
* crypto:generate-signature() (only for XML data for now)
27-
* crypto:validate-signature() (only for XML data for now)
107+
There are two breaking changes for users upgrading from v6:
28108

29-
### Currently implemented algorithms
109+
#### 1. `crypto:encrypt` / `crypto:decrypt` output format
30110

31-
* For crypto:hash(): "MD5", "SHA-1", "SHA-256", "SHA-384", "SHA-512".
32-
* For crypto:hmac(): "HMAC-MD5", "HMAC-SHA-1", "HMAC-SHA-256", "HMAC-SHA-384", "HMAC-SHA-512".
111+
| | v6 (expath-crypto-module) | v7 (exist-crypto) |
112+
|---|---|---|
113+
| **encrypt output** | Dash-separated bytes: `"51-143-171-200-..."` | `xs:base64Binary` |
114+
| **decrypt input** | Dash-separated bytes | `xs:base64Binary` |
115+
116+
**Impact:** Data encrypted with v6 **cannot be decrypted with v7** (and vice versa). The wire format is different.
117+
118+
**Migration:** If you have stored encrypted data from v6, decrypt it with v6 first, then re-encrypt with v7. For data encrypted in transit (e.g., JWT tokens), both sides must use the same version.
119+
120+
#### 2. DSA_SHA1 signatures forbidden
121+
122+
Java 21+ forbids DSA_SHA1 in secure signature validation. Use `RSA_SHA256` instead:
123+
124+
```xquery
125+
(: v6 — worked but now forbidden by Java 21+ :)
126+
crypto:generate-signature($doc, "inclusive", "SHA1", "DSA_SHA1", "dsig", "enveloped")
127+
128+
(: v7 — use RSA_SHA256 instead :)
129+
crypto:generate-signature($doc, "inclusive", "SHA256", "RSA_SHA256", "dsig", "enveloped")
130+
```
33131

34-
### Documentation
132+
### Improvements over v6
35133

36-
For the latest version of the specification for this module see [http://expath.org/spec/crypto/editor](http://expath.org/spec/crypto/editor).
134+
- **Binary HMAC input**: `crypto:hmac` now accepts `xs:base64Binary` and `xs:hexBinary` for `$data` and `$key`
135+
- **Flexible algorithm names**: lowercase (`sha256`), bare (`SHA256`), hyphenated (`SHA-256`), and prefixed (`HMAC-SHA-256`) all work
136+
- **AES key sizes**: 128, 192, and 256-bit keys supported (v6 only supported 128)
137+
- **RSA_SHA256**: Added as a signature algorithm (v6 only had RSA_SHA1 and DSA_SHA1)
138+
- **Zero dependencies**: No external `crypto-java` library needed
139+
- **102 tests**: JUnit + XQSuite, including BaseX cross-compatibility vectors
37140

38-
The implementation follows this specification.
141+
## Conformance
39142

40-
### Unit Tests
143+
### EXPath Cryptographic Module 1.0
41144

42-
Unit Tests use [XQSuite](https://exist-db.org/exist/apps/doc/xqsuite) and can be found in the [src/test/xquery/crypto folder](src/test/xquery/crypto/).
145+
Implements §2 (hash), §3 (hmac), §5 (encrypt), §6 (decrypt), §7 (generate-signature), §8 (validate-signature). Not implemented: §4 (key management), §9 (secure store) — these are marked "TBD" in the spec.
43146

44-
To run tests:
147+
### EXPath Cryptographic Module Editor's Draft
148+
149+
The editor's draft consolidates function parameters into maps (`$parameters as map(xs:string, item())`). We support this via the 2-param map-based `generate-signature` arity alongside the positional arities for backward compatibility.
150+
151+
### [EXPath Crypto 2018 CG Final Report](https://github.com/claudius108/expath-cg/blob/master/specs/crypto/crypto-new.html)
152+
153+
This November 2018 revision by Claudius Teodorescu adds several features we implement:
154+
- **`crypto:list-providers`/`list-services`/`list-algorithms`** — provider introspection functions returning maps and arrays. Implemented but currently disabled pending an eXist-db binary search fix for zero-param arities in ordered module registries.
155+
- **Algorithm name aliases** — Java-style names `DSAwithSHA1`, `RSAwithSHA1` accepted alongside `DSA_SHA1`, `RSA_SHA1`.
156+
- **XML Canonicalization 1.1**`inclusive-1.1` and `inclusive-with-comments-1.1` options.
157+
- **Map-based `generate-signature`** — 2-param `($data, $parameters as map(*))` arity.
158+
159+
### BaseX Cryptographic Functions
160+
161+
Function signatures match BaseX for the common arities: `crypto:hmac` (4-param), `crypto:encrypt` (4-param), `crypto:decrypt` (4-param), `crypto:generate-signature` (6+2 optional), `crypto:validate-signature` (1-param). Algorithm names accept BaseX's lowercase convention.
162+
163+
## Build
45164

46165
```bash
47-
$ mvn verify
48-
```
166+
JAVA_HOME=/path/to/java-21 mvn clean package -DskipTests
167+
```
168+
169+
Run tests (102 total — requires exist-core in local Maven repo):
170+
171+
```bash
172+
mvn test -Pintegration-tests
173+
```
174+
175+
## License
176+
177+
[GNU Lesser General Public License v2.1](https://opensource.org/licenses/LGPL-2.1)

0 commit comments

Comments
 (0)