diff --git a/cmd/sign1util/main.go b/cmd/sign1util/main.go index efbf5a4..0e5f62c 100644 --- a/cmd/sign1util/main.go +++ b/cmd/sign1util/main.go @@ -41,11 +41,16 @@ func checkCoseSign1(inputFilename string, chainFilename string, didString string fmt.Fprintf(os.Stdout, "pubcert: %s\n", unpacked.Pubcert) fmt.Fprintf(os.Stdout, "payload:\n%s\n", string(unpacked.Payload[:])) } + + if len(chainPEMString) == 0 { + chainPEMString = unpacked.ChainPem + } + // Only resolve when the caller explicitly asked. Callers like `did-x509 -in`, + // `leaf -in`, `print -in` pass an empty didString and don't want DID + // resolution. The `check` command supplies its own fallback below. if len(didString) > 0 { - if len(chainPEMString) == 0 { - chainPEMString = unpacked.ChainPem - } - didDoc, err := didx509resolver.Resolve(chainPEMString, didString, true) + var didDoc string + didDoc, err = didx509resolver.Resolve(chainPEMString, didString, true) if err == nil { fmt.Fprintf(os.Stdout, "DID resolvers passed:\n%s\n", didDoc) } else { @@ -53,6 +58,7 @@ func checkCoseSign1(inputFilename string, chainFilename string, didString string fmt.Fprintf(os.Stdout, "DID resolvers failed: err: %s\n", err.Error()) } } + return unpacked, err } @@ -173,15 +179,23 @@ var checkCmd = cli.Command{ }, }, Action: func(ctx *cli.Context) error { - _, err := checkCoseSign1( + didString := ctx.String("did") + unpacked, err := checkCoseSign1( ctx.String("in"), ctx.String("chain"), - ctx.String("did"), + didString, ctx.Bool("verbose"), ) if err != nil { return fmt.Errorf("failed check: %w", err) } + // If no explicit -did was given, validate the issuer embedded in the + // COSE document against the chain. + if len(didString) == 0 && len(unpacked.Issuer) > 0 { + if _, err := didx509resolver.Resolve(unpacked.ChainPem, unpacked.Issuer, true); err != nil { + return fmt.Errorf("failed check (issuer from cose %q): %w", unpacked.Issuer, err) + } + } return nil }, } diff --git a/pkg/cosesign1/Makefile b/pkg/cosesign1/Makefile index 8c98213..6527ca1 100755 --- a/pkg/cosesign1/Makefile +++ b/pkg/cosesign1/Makefile @@ -24,12 +24,11 @@ # -# note test-fail is expected to fail - -AUTOPARSE_CHAIN:=0 +# Opaque label written into the COSE `iss` header. It is NOT validated against +# the chain at create time; the Go unit test asserts the round-trip preserves +# this exact string, so don't change it without updating the test. ISSUER_DID:="TestIssuer" FEED:="TestFeed" -DID_FINGERPRINT:="" all: chain.pem cose test-fail test-pass @@ -38,11 +37,6 @@ cose: infra.rego.cose %.pem: $(MAKE) -f Makefile.certs chain.pem -ifeq "$(AUTOPARSE_CHAIN)" "1" -ISSUER_DID = $(shell ./sign1util did-x509 -chain chain.pem -policy cn) -DID_FINGERPRINT = $(shell ./sign1util did-x509 -chain chain.pem -policy cn | cut -d: -f5) -endif - # from these media types have to match containerd. The also need to change and the security policy one ought to be x-ms-ccepolicy-frag # fragment atrifact type = application/x-ms-ccepolicy-frag # fragment media type = application/cose-x509+rego @@ -74,13 +68,13 @@ show: sign1util didx509: chain.pem sign1util ./sign1util did-x509 -chain chain.pem -i 1 -policy "subject:CN:Test Leaf (DO NOT TRUST)" -verbose -info: chain.pem sign1util - @echo "ISSUER_DID: $(ISSUER_DID)" - @echo "DID_FINGERPRINT: $(DID_FINGERPRINT)" - -# for this to pass the did:x509 fingerprint (RgpNsHOK5hPlCAfTtiGY_BcDhFRxQbJnhlxNDhxps6U here) needs to be the one output from make print -did-check: chain.pem infra.rego.cose sign1util info - ./sign1util check -in infra.rego.cose -did $(ISSUER_DID) +# did-check derives the REAL did:x509 from chain.pem at run time and resolves +# it against the chain. Fails loudly if did-x509 returns empty. +did-check: chain.pem infra.rego.cose sign1util + @did="$$(./sign1util did-x509 -chain chain.pem -policy cn)"; \ + test -n "$$did" || { echo "did-x509 returned empty - check chain.pem"; exit 1; }; \ + echo "did-check: using did=$$did"; \ + ./sign1util check -in infra.rego.cose -did "$$did" # For normal workflow start from the chain.pem, here we'd take the chain from inside the cose sign1 doc, eg to manually confirm it is # as otherwise expected (ie that the issuer DID matches the chain) or to shortcut getting a DID from a cose document. @@ -92,12 +86,18 @@ did-from-cose: sign1util infra.rego.cose # note that since the infra.rego.cose is actually good the first part of the check will report a pass "checkCoseSign1 passed" # expect "DID resolvers failed: err: DID verification failed: unexpected certificate fingerprint" +# The recipe is expected to fail at the tool level; invert the exit code so the make target succeeds. did-fail-fingerprint: chain.pem sign1util infra.rego.cose - ./sign1util check -in infra.rego.cose -did did:x509:0:sha256:XXXi_nuWegx4NiLaeGabiz36bDUhDDiHEFl8HXMA_4o::subject:CN:Test+Leaf+%28DO+NOT+TRUST%29 + ! ./sign1util check -in infra.rego.cose -did did:x509:0:sha256:XXXi_nuWegx4NiLaeGabiz36bDUhDDiHEFl8HXMA_4o::subject:CN:Test+Leaf+%28DO+NOT+TRUST%29 # expect "DID resolvers failed: err: DID verification failed: invalid subject value: CN=Test XXXX (DO NOT TRUST)" +# Builds a DID with the REAL fingerprint but a WRONG subject; recipe must still +# fail at the tool level - `!` inverts that into a make-level success. did-fail-subject: chain.pem sign1util infra.rego.cose - ./sign1util check -in infra.rego.cose -did did:x509:0:sha256:$(DID_FINGERPRINT)::subject:CN:Test+XXXX+%28DO+NOT+TRUST%29 + @fp="$$(./sign1util did-x509 -chain chain.pem -policy cn | cut -d: -f5)"; \ + test -n "$$fp" || { echo "could not derive fingerprint - check chain.pem"; exit 1; }; \ + ! ./sign1util check -in infra.rego.cose \ + -did "did:x509:0:sha256:$$fp::subject:CN:Test+XXXX+%28DO+NOT+TRUST%29" did-fail: did-fail-subject did-fail-fingerprint diff --git a/pkg/cosesign1/Makefile.certs b/pkg/cosesign1/Makefile.certs index 9a0ce35..e2452a7 100644 --- a/pkg/cosesign1/Makefile.certs +++ b/pkg/cosesign1/Makefile.certs @@ -8,12 +8,12 @@ all: chain.pem root.cert.pem: root.private.pem openssl req -new -key $< -out $@.tmp.csr -subj "/CN=Test Root CA (DO NOT TRUST)" -addext 'basicConstraints=critical,CA:TRUE' -addext 'keyUsage=digitalSignature,keyCertSign' - openssl x509 -req -days 365 -in $@.tmp.csr -signkey $< -out $@ -CAcreateserial -extfile cert.extensions.cfg + openssl x509 -req -days 3650 -in $@.tmp.csr -signkey $< -out $@ -CAcreateserial -extfile cert.extensions.cfg rm -rf $@.tmp.csr intermediate.cert.pem: intermediate.private.pem | root.private.pem openssl req -new -key $< -out $@.tmp.csr -subj "/CN=Test Intermediate CA (DO NOT TRUST)" -addext 'basicConstraints=critical,CA:TRUE' -addext 'keyUsage=digitalSignature,keyCertSign' - openssl x509 -req -days 365 -in $@.tmp.csr -CA ${subst private,cert,$|} -CAkey $| -out $@ -CAcreateserial -extfile cert.extensions.cfg + openssl x509 -req -days 1825 -in $@.tmp.csr -CA ${subst private,cert,$|} -CAkey $| -out $@ -CAcreateserial -extfile cert.extensions.cfg rm $@.tmp.csr leaf.cert.pem: leaf.private.pem | intermediate.private.pem