Skip to content

Commit 4cbdc5d

Browse files
committed
Add e2e tests for client-signing-algorithms server flag
Signed-off-by: Riccardo Schirone <[email protected]>
1 parent 8fe29f9 commit 4cbdc5d

File tree

15 files changed

+363
-3
lines changed

15 files changed

+363
-3
lines changed

.github/workflows/main.yml

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
# Copyright 2021 The Sigstore Authors.
32
#
43
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,11 +18,11 @@ on:
1918
push:
2019
branches:
2120
- main
22-
- 'release-**'
21+
- "release-**"
2322
pull_request:
2423
branches:
2524
- main
26-
- 'release-**'
25+
- "release-**"
2726

2827
permissions:
2928
contents: read
@@ -227,6 +226,31 @@ jobs:
227226
name: Docker Compose logs
228227
path: /tmp/*docker-compose.log
229228

229+
client-algorithms-e2e:
230+
runs-on: ubuntu-latest
231+
needs: build
232+
233+
steps:
234+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
235+
with:
236+
persist-credentials: false
237+
- name: Docker Build
238+
run: docker compose build
239+
- name: Extract version of Go to use
240+
run: echo "GOVERSION=$(awk -F'[:@]' '/FROM golang/{print $2; exit}' Dockerfile)" >> $GITHUB_ENV
241+
- uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
242+
with:
243+
go-version: ${{ env.GOVERSION }}
244+
245+
- name: Test for supported client algorithms
246+
run: ./tests/client-algos-e2e-test.sh
247+
- name: Upload logs if they exist
248+
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
249+
if: failure()
250+
with:
251+
name: Docker Compose logs
252+
path: /tmp/*docker-compose.log
253+
230254
harness:
231255
runs-on: ubuntu-latest
232256
needs: build

tests/client-algos-e2e-test.sh

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
#!/bin/bash
2+
#
3+
# Copyright 2025 The Sigstore Authors.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
docker_compose="docker compose"
18+
if ! ${docker_compose} version >/dev/null 2>&1; then
19+
docker_compose="docker-compose"
20+
fi
21+
22+
echo "* starting services with default client signing algorithms"
23+
${docker_compose} up -d
24+
25+
echo "* building CLI"
26+
go build -o rekor-cli ./cmd/rekor-cli
27+
REKOR_CLI=$(pwd)/rekor-cli
28+
29+
function waitForRekorServer () {
30+
echo -n "* waiting up to 60 sec for system to start"
31+
count=0
32+
33+
until [ $(docker ps -a | grep -c "(healthy)") == 3 ];
34+
do
35+
if [ $count -eq 6 ]; then
36+
echo "! timeout reached"
37+
exit 1
38+
else
39+
echo -n "."
40+
sleep 10
41+
let 'count+=1'
42+
fi
43+
done
44+
45+
echo
46+
}
47+
48+
function check_log_index () {
49+
logIndex=$1
50+
# make sure we can get this log index from rekor
51+
$REKOR_CLI get --log-index $logIndex --rekor_server http://localhost:3000
52+
# make sure the entry index matches the log index
53+
gotIndex=$($REKOR_CLI get --log-index $logIndex --rekor_server http://localhost:3000 --format json | jq -r .LogIndex)
54+
if [[ "$gotIndex" == $logIndex ]]; then
55+
echo "New entry has expected virtual log index $gotIndex"
56+
else
57+
echo "FAIL: expected virtual log index $logIndex, got $gotIndex"
58+
exit 1
59+
fi
60+
}
61+
62+
function collectLogsOnFailure () {
63+
if [[ "$1" -ne "0" ]]; then
64+
echo "failure detected, collecting docker-compose logs"
65+
${docker_compose} logs --no-color > /tmp/docker-compose.log
66+
exit $1
67+
elif ${docker_compose} logs --no-color | grep -q "panic: runtime error:" ; then
68+
# if we're here, we found a panic
69+
echo "failing due to panics detected in logs"
70+
${docker_compose} logs --no-color > /tmp/docker-compose.log
71+
exit 1
72+
fi
73+
exit 0
74+
}
75+
trap "collectLogsOnFailure \$?" EXIT
76+
77+
# Create temp directory for test artifacts
78+
REKORTMPDIR="$(mktemp -d -t rekor_test.XXXXXX)"
79+
touch $REKORTMPDIR.rekor.yaml
80+
trap "rm -rf $REKORTMPDIR" EXIT
81+
82+
waitForRekorServer
83+
84+
# Test default behavior - should accept all supported algorithms
85+
echo "* testing default client signing algorithms behavior"
86+
87+
# Test with ECDSA
88+
pushd tests/client-algos-testdata || exit 1
89+
if ! $REKOR_CLI upload --artifact file1 --artifact-hash "$(sha256sum file1 | awk '{ print $1 }')" --signature file1.ec.sig --pki-format=x509 --public-key=ec_public.pem --rekor_server http://localhost:3000 --type hashedrekord; then
90+
echo "! ERROR: ECDSA upload failed"
91+
exit 1
92+
else
93+
echo "* successfully uploaded entry with ECDSA"
94+
fi
95+
popd || exit 1
96+
check_log_index 0
97+
98+
pushd tests/client-algos-testdata || exit 1
99+
if ! $REKOR_CLI upload --artifact file2 --artifact-hash "$(sha256sum file2 | awk '{ print $1 }')" --signature file2.rsa.sig --pki-format=x509 --public-key=rsa_public.pem --rekor_server http://localhost:3000 --type hashedrekord; then
100+
echo "! ERROR: RSA upload failed"
101+
exit 1
102+
else
103+
echo "* successfully uploaded entry with RSA"
104+
fi
105+
popd || exit 1
106+
check_log_index 1
107+
108+
# Stop the rekor server
109+
echo "* stopping rekor server to reconfigure client signing algorithms"
110+
${docker_compose} stop rekor-server
111+
112+
# Create a new compose file with restricted algorithms
113+
COMPOSE_FILE=$REKORTMPDIR/docker-compose-restricted-algos.yaml
114+
cat << EOF > $COMPOSE_FILE
115+
version: '3.4'
116+
services:
117+
rekor-server:
118+
build:
119+
context: .
120+
target: "deploy"
121+
command: [
122+
"rekor-server",
123+
"serve",
124+
"--trillian_log_server.address=trillian-log-server",
125+
"--trillian_log_server.port=8090",
126+
"--redis_server.address=redis-server",
127+
"--redis_server.port=6379",
128+
"--rekor_server.address=0.0.0.0",
129+
"--rekor_server.signer=memory",
130+
"--enable_attestation_storage",
131+
"--attestation_storage_bucket=file:///var/run/attestations",
132+
"--client-signing-algorithms=rsa-sign-pkcs1-2048-sha256,ed25519-ph",
133+
# Uncomment this for production logging
134+
# "--log_type=prod",
135+
]
136+
volumes:
137+
- "/var/run/attestations:/var/run/attestations:z"
138+
restart: always # keep the server running
139+
ports:
140+
- "3000:3000"
141+
- "2112:2112"
142+
healthcheck:
143+
test: ["CMD", "curl", "-f", "http://localhost:3000/ping"]
144+
interval: 10s
145+
timeout: 3s
146+
retries: 3
147+
start_period: 5s
148+
EOF
149+
150+
echo "* starting rekor server with restricted client signing algorithms (rsa-sign-pkcs1-2048-sha256, ed25519-ph only)"
151+
${docker_compose} -f $COMPOSE_FILE --project-directory=$PWD up -d
152+
waitForRekorServer
153+
154+
# Test with RSA - should succeed
155+
pushd tests/client-algos-testdata || exit 1
156+
if ! $REKOR_CLI upload --artifact file1 --artifact-hash "$(sha256sum file1 | awk '{ print $1 }')" --signature file1.rsa.sig --pki-format=x509 --public-key=rsa_public.pem --rekor_server http://localhost:3000 --type hashedrekord; then
157+
echo "! ERROR: RSA upload failed"
158+
exit 1
159+
else
160+
echo "* successfully uploaded entry with RSA"
161+
fi
162+
popd || exit 1
163+
check_log_index 0
164+
165+
# Test with ECDSA - should fail
166+
echo "* testing ECDSA upload with restricted algorithms"
167+
pushd tests/client-algos-testdata || exit 1
168+
output=$($REKOR_CLI upload --artifact file1 --artifact-hash "$(sha256sum file1 | awk '{ print $1 }')" --signature file1.ec.sig --pki-format=x509 --public-key=ec_public.pem --rekor_server http://localhost:3000 --type hashedrekord 2>&1)
169+
if [ $? -eq 0 ]; then
170+
echo "! ERROR: ECDSA upload should have failed but succeeded"
171+
exit 1
172+
elif ! echo "$output" | grep -q "entry algorithms are not allowed"; then
173+
echo "! ERROR: ECDSA upload failed but with unexpected error message:"
174+
echo "$output"
175+
exit 1
176+
else
177+
echo "* ECDSA upload failed as expected with restricted algorithms"
178+
fi
179+
popd || exit 1
180+
181+
# Test with RSA and sha512 hash - should fail
182+
echo "* testing RSA and sha512 hash upload with restricted algorithms"
183+
pushd tests/client-algos-testdata || exit 1
184+
output=$($REKOR_CLI upload --artifact file2 --artifact-hash "$(sha512sum file2 | awk '{ print $1 }')" --signature file2.rsa512.sig --pki-format=x509 --public-key=rsa_public.pem --rekor_server http://localhost:3000 --type hashedrekord 2>&1)
185+
if [ $? -eq 0 ]; then
186+
echo "! ERROR: RSA with SHA512 upload should have failed but succeeded"
187+
exit 1
188+
elif ! echo "$output" | grep -q "entry algorithms are not allowed"; then
189+
echo "! ERROR: RSA with SHA512 upload failed but with unexpected error message:"
190+
echo "$output"
191+
exit 1
192+
else
193+
echo "* RSA with SHA512 upload failed as expected with restricted algorithms"
194+
fi
195+
popd || exit 1
196+
197+
# Test with ED25519-PH
198+
echo "* testing ED25519-PH upload with restricted algorithms"
199+
pushd tests/client-algos-testdata || exit 1
200+
if ! $REKOR_CLI upload --artifact file1 --artifact-hash "$(sha512sum file1 | awk '{ print $1 }')" --signature file1.ed25519ph.sig --pki-format=x509 --public-key=ed25519_public.pem --rekor_server http://localhost:3000 --type hashedrekord ; then
201+
echo "! ERROR: ED25519-PH upload failed"
202+
exit 1
203+
else
204+
echo "* successfully uploaded entry with ED25519-PH"
205+
fi
206+
popd || exit 1
207+
208+
209+
# Test with ED25519 no ph
210+
echo "* testing regular ED25519 upload with restricted algorithms"
211+
pushd tests/client-algos-testdata || exit 1
212+
output=$($REKOR_CLI upload --artifact file1 --artifact-hash "$(sha512sum file1 | awk '{ print $1 }')" --signature file1.ed25519.sig --pki-format=x509 --public-key=ed25519_public.pem --rekor_server http://localhost:3000 --type hashedrekord 2>&1)
213+
if [ $? -eq 0 ]; then
214+
echo "! ERROR: ED25519 upload should have failed but succeeded"
215+
exit 1
216+
elif ! echo "$output" | grep -q "ed25519: invalid signature"; then
217+
echo "! ERROR: ED25519 upload failed but with unexpected error message:"
218+
echo "$output"
219+
exit 1
220+
else
221+
echo "* ED25519 upload failed as expected with restricted algorithms"
222+
fi
223+
popd || exit 1
224+
225+
226+
echo "* all tests passed successfully!"

tests/client-algos-testdata/README.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# How to recreate files in this directory
2+
3+
## RSA
4+
5+
```bash
6+
openssl genrsa -out rsa_private.pem 2048
7+
openssl rsa -in rsa_private.pem -pubout -out rsa_public.pem
8+
openssl dgst -sha256 -sign rsa_private.pem -out file1.rsa.sig file1
9+
openssl dgst -sha512 -sign rsa_private.pem -out file2.rsa.sig file2
10+
```
11+
12+
## ECDSA
13+
14+
```bash
15+
openssl ecparam -name secp256k1 -genkey -noout -out ec_private.pem
16+
openssl ec -in ec_private.pem -pubout -out ec_public.pem
17+
openssl dgst -sha256 -sign ec_private.pem -out file1.ec.sig file1
18+
openssl dgst -sha512 -sign ec_private.pem -out file2.ec.sig file2
19+
```
20+
21+
## ED25519/ED25519-PH
22+
23+
```bash
24+
openssl genpkey -algorithm ed25519 -out ed25519_private.pem
25+
openssl pkey -in ed25519_private.pem -pubout -out ed25519_public.pem
26+
```
27+
28+
## ED25519 signature
29+
30+
```bash
31+
openssl pkeyutl -sign -inkey ed25519_private.pem -rawin -in file1 -out file1.ed25519.sig
32+
```
33+
34+
## ED25519-PH signature
35+
36+
Generate the signature with the following Go program:
37+
38+
```golang
39+
package main
40+
41+
import (
42+
"crypto"
43+
"crypto/ed25519"
44+
"crypto/rand"
45+
"crypto/sha512"
46+
"crypto/x509"
47+
"encoding/pem"
48+
"flag"
49+
"fmt"
50+
"os"
51+
)
52+
53+
func loadKeyFromFile(filename string) ([]byte, error) {
54+
data, _ := os.ReadFile(filename)
55+
block, _ := pem.Decode(data)
56+
return block.Bytes, nil
57+
}
58+
59+
func signMessage(privateKey ed25519.PrivateKey, message []byte) ([]byte, error) {
60+
h := sha512.New()
61+
h.Write(message)
62+
messageHash := h.Sum(nil)
63+
64+
return privateKey.Sign(rand.Reader, messageHash, &ed25519.Options{
65+
Hash: crypto.SHA512,
66+
})
67+
}
68+
69+
func main() {
70+
privateKeyFile := flag.String("private-key", "private.pem", "Private key file")
71+
messageFile := flag.String("message", "", "Message file")
72+
signatureFile := flag.String("signature", "", "Signature file")
73+
74+
flag.Parse()
75+
76+
privKeyBytes, _ := loadKeyFromFile(*privateKeyFile)
77+
message, _ := os.ReadFile(*messageFile)
78+
privKey, _ := x509.ParsePKCS8PrivateKey(privKeyBytes)
79+
signature, _ := signMessage(privKey.(ed25519.PrivateKey), message)
80+
os.WriteFile(*signatureFile, signature, 0644)
81+
fmt.Printf("Message signed successfully:\n Signature saved to: %s\n", *signatureFile)
82+
return
83+
}
84+
```
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMOcTfRBS9jiXM81FZ8gm/1+omeMw
3+
mn/347/556g/lriS72uMhY9LcT+5UJ6fGBglr5Z8L0JNSuasyed9OtaRvw==
4+
-----END PUBLIC KEY-----
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MCowBQYDK2VwAyEAOzUw7NeN1swAooEM99Xuz+Nw7ajYA/5csFlX0YlcHdY=
3+
-----END PUBLIC KEY-----

tests/client-algos-testdata/file1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
file1
72 Bytes
Binary file not shown.

tests/client-algos-testdata/file1.ed25519.sig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
萙흨䳲䱤ⰼ崊菷ਯ쟙拻傳囓㨿螰멆웲杽�菲윆퟉♄銹ච돗㪬북륜㩗⌵‎
2+
��
3+
/��b�P�V�:?���F��g}ج�<�����&D�����:����\:W#5 
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3hu¢VŸÜ„?rÂíD‰‹?çyÊA7T›Þ™Ç¤¨ö6¼¹÷M„¾ä¢±¿®70À&±aƒÔ@sTç
256 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)