Publish the signing rules as a library - #126
Open
beetlebugorg wants to merge 8 commits into
Open
Conversation
libmoddims_sign holds the /dims4/ and /dims5/ rules behind a C99 header that links libcrypto. A caller outside the module builds against it without APR and without httpd. One object library compiles the source once, and the archive and the shared object are built from those objects. Two calls sign a URL. The rest of the header is what the module and a verifier need: the escape, the canonical query, the two digests, the two comparisons, and the field check. No other target links it yet.
test/unit/test_sign.c covers the escape, the canonical query, the two digests, the two comparisons, and the two URL signers. Each digest has a known vector. One case per clause the header names under DIMS_SIGN_BAD_URL, and one for DIMS_SIGN_BAD_FIELD. A failed call leaves the out parameter untouched, and dims_sign_free accepts NULL.
src/signature.c is three wrappers over the library: dims_signature, dims_signature_equal, and dims_signature_field_ok. src/handler.c calls dims_sign_dims4_digest and dims_sign_dims4_equal. The module keeps its own prefix handling. It reads the commands out of r->uri and passes them to dims_signature. test/unit/test_signature.c is removed. test/unit/test_sign.c covers the same rules against the library.
dims-sign signs a URL, prints the /dims5/ message behind one, and compares the signature a URL holds against the one the key produces. The module logs "Key mismatch" without the digests. This prints both. The key comes from --key-file or from DIMS_SIGNING_KEY. A key given on the command line is visible to every user of the machine through ps, so there is no --key flag. --message requires --dims5, because a /dims4/ message contains the client secret. sign/src/dims_sign_internal.h declares dims_sign_dims4_message for the command and stays out of the install. --fixture fills in the computed fields of the signing fixtures.
test/fixtures/signing.tsv holds thirty eight records. A person writes case, endpoint, prefix, key, and input. dims-sign --fixture writes signed, query, message, and error. The keys, the client id, and the expiry match test/conf/dims-test.conf. test/lib/fixtures.c reads the file for both suites. test/unit/test_fixtures.c asserts every field against the library and names the record that differs. A second case runs dims-sign --fixture over the file and compares the output to it line by line.
One case reads test/fixtures/signing.tsv and sends each signed URL to the running module. It scrapes dims_signature_checks_total before and after, then asserts that the ok counter rose by the number of requests and the mismatch counter did not change. A signature refusal and a bad crop return the same status, so the status does not separate them. A record with an error has no signed URL. A record outside /dims4/ and /dims5/ is skipped, because the module reads a fixed seven characters past the start of the path. A record holding eurl is skipped, because the module decrypts eurl and a ciphertext contains a fresh nonce.
The install writes dims_sign.h under include/dims, the archive and the shared library under lib, dims-sign under bin, and dims-sign.pc under lib/pkgconfig. A caller builds against it with pkg-config --cflags --libs dims-sign. docker/Dockerfile copies only libmod_dims.so out of the build stage, so the server image has no dims-sign. test/endurance/sign.c calls the library for its query escape and its two digests. It keeps the path escape, which writes a space as %20, and the encryption helpers. docs/docs/clients/ describes the library and the command, and the two endpoint pages link to it. dims4.md states that a _keys value goes into the message as it appears in the query string, and that a plus in the image URL signs as a space.
libmoddims_sign gains the key derivation and the two eurl ciphers. dims_sign_dims5_eurl_url and dims_sign_dims4_eurl_url sign a URL and put the encrypted source in place of url. The signature covers the plain image URL, so the server verifies the request after it decrypts. /dims5/ derives with HKDF-SHA256 under the salt go-dims and encrypts with AES-128-GCM. /dims4/ derives from SHA-1 of the client secret and encrypts with AES-128-ECB, or with GCM under DimsEncryptionAlgorithm. The value is percent encoded on /dims5/ and undecoded on /dims4/, because each endpoint reads the parameter that way. test/fixtures/signing.tsv gains four eurl records, each holding a ciphertext and the plain URL it decrypts to. Two request cases send a URL the library encrypted to the running module. test/endurance/sign.c calls the library for its derivations and its ECB encryption, and keeps its GCM, where the IV comes from the run's seed.
This was referenced Sep 9, 2026
beetlebugorg
added this pull request to stack #129
September 9, 2026 23:45
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
libmoddims_signholds the/dims4/and/dims5/signing rules behind a C99 header that links libcrypto. The module compiles the same source:src/signature.cis three wrappers over it, andsrc/handler.ccalls the/dims4/digest and comparison. The install writes the header underinclude/dims, the archive and the shared library underlib,dims-signunderbin, anddims-sign.pcunderlib/pkgconfig.dims-signsigns a URL, prints the/dims5/message behind one, and compares the signature a URL holds against the one the key produces.The library also encrypts.
dims_sign_dims5_eurl_urlanddims_sign_dims4_eurl_urlsign a URL and put the encrypted source in place ofurl./dims5/derives with HKDF-SHA256 under the saltgo-dimsand encrypts with AES-128-GCM./dims4/derives from SHA-1 of the client secret and encrypts with AES-128-ECB, or with GCM underDimsEncryptionAlgorithm.test/fixtures/signing.tsvholds forty two records. A signing record has a signed URL, a canonical query, and a message. An eurl record has a ciphertext and the plain URL it decrypts to. Two unit cases assert every field against the library and check thatdims-sign --fixturereproduces the file. One request case sends each signed URL to the running module and readsdims_signature_checks_totalto confirm every one verified, and two more send a URL the library encrypted.test/endurance/sign.ccalls the library for its query escape and its two digests.Why
Issue #124. Four copies of these rules exist here:
src/signature.c,src/handler.c,test/lib/signing.c, andtest/endurance/sign.c. go-dims holds a fifth. A Go client and a Java client follow, and the fixture file is what all three check themselves against.TestDims5EurlUrlVerifiesandTestDims4EurlUrlVerifiesprove the two sides meet: the library encrypts, and the module decrypts across a request. Theeurl-dims5-gcmfixture value came from the independent implementation intest/compose.yaml, andeurl-dims4-ecbcame fromopenssl enc -aes-128-ecb.The request case found a disagreement.
src/handler.cwrites every plus in a/dims4/image URL as a space, and the documentation did not state it.docs/docs/endpoints/dims4.mdnow does, along with the rule that a_keysvalue goes into the message as it appears in the query string.Verify
326 of 326 pass. The existing
/dims4/and/dims5/cases are unchanged, so a pass means the module emits the same bytes.128 in-process cases under AddressSanitizer, UndefinedBehaviorSanitizer, and LeakSanitizer.
To see the fixture file work as a golden, change one character of a signature in
test/fixtures/signing.tsv.unit.TestSigningFixturesnames the record andunit.TestSigningFixtureRoundTripnames the line.