Packages · supply-chain

k2gl/signed-note

Read and write signed notes (Go sumdb / Rekor checkpoint format).

Parse and verify the note-with-signatures format transparency logs use for checkpoints.

k2gl/signed-note version on Packagist k2gl/signed-note total downloads k2gl/signed-note license

Install

composer require k2gl/signed-note
PHP
>=8.1
k2gl dependencies
k2gl/dsse

Reach for it when

  • You parse or verify signed notes (Go checksum database, Rekor checkpoints).
  • You’re working with transparency-log checkpoints directly.

Look elsewhere when

  • You want a general Sigstore bundle — use sigstore-bundle.

k2gl/signed-note

Parse, verify and sign signed notes in PHP — the format Go's sumdb and transparency-log checkpoints (Sigstore Rekor) use. A note is some text, a blank line, then one or more signature lines:

If you think cryptography is the answer to your problem,
then you don't know what your problem is.

— PeterNeumann x08go/ZJkuBS9UG/SffcvIAQxVBtiFupLLr8pAcElZInNIuGUgYN1FFYC2pZSNXgKvqfqdngotpRZb6KE6RyyBwJnAM=

Each signature line is — <name> <base64>, where the base64 decodes to a 4-byte key hash and the raw signature over the text. The cryptography is delegated to [k2gl/dsse].

Install

composer require k2gl/signed-note

Requires PHP 8.1+ with ext-sodium (Ed25519); ext-openssl is needed only for ECDSA-signed notes such as Rekor v1 checkpoints.

Verify

Give a NoteVerifier the keys you trust; it returns the signatures that verify and throws if none do.

use K2gl\SignedNote\Note;
use K2gl\SignedNote\NoteVerifier;
use K2gl\SignedNote\VerifierKey;

$key = VerifierKey::fromString('PeterNeumann+c74f20a3+ARpc2QcUPDhMQegwxbzhKqiBfsVkmqq/LDE4izWy10TW');

$verified = (new NoteVerifier($key))->verify(Note::parse($envelope));
// $verified is the list of NoteSignature that checked out; SignatureVerificationFailed otherwise.

A Rekor checkpoint is a note signed by the log's key. Load that key from its PEM — the key hash is derived the Sigstore way (first four bytes of SHA-256 of the DER key), and any RSA/ECDSA/Ed25519 key works:

$log = VerifierKey::fromPem('rekor.sigstore.dev - 1193050959916656506', $logPublicKeyPem);
(new NoteVerifier($log))->verify(Note::parse($checkpoint));

Sign

use K2gl\SignedNote\SignerKey;

$signer = SignerKey::fromString('PRIVATE+KEY+PeterNeumann+c74f20a3+AYEKFALVFGyNhPJEMzD1QIDr+Y7hfZx09iUvxdXHKDFz');

$note = $signer->sign("hello\n"); // text must end with a newline
echo $note;                       // renders the note with its signature line

The output is byte-for-byte what Go's note.Sign produces (checked against the golang.org/x/mod/sumdb/note test vectors).

Design

  • Format only. Note is the generic note; it does not interpret checkpoint fields (origin, tree size, root hash). Parse those from signedText() yourself.
  • Fail-closed. A verified result means a trusted key signed the exact text.
  • Ed25519 is the standard. VerifierKey::fromString() / SignerKey handle the Ed25519 key strings from Go's note package; fromPem() covers ECDSA/RSA logs.

License

MIT.

API

Public classes and methods, generated from the source.

K2gl\SignedNote\Exception\InvalidNoteException class

K2gl\SignedNote\Exception\SignatureVerificationFailed class

K2gl\SignedNote\Exception\SignedNoteException interface

K2gl\SignedNote\Note class

  • __construct( public readonly string $text, public readonly array $signatures, )
  • parse(string $envelope): self
  • signedText(): string
  • signatures(): array
  • __toString(): string

K2gl\SignedNote\NoteSignature class

  • __construct( public readonly string $name, public readonly string $keyHash, public readonly string $signature, )

K2gl\SignedNote\NoteVerifier class

  • __construct(VerifierKey ...$keys)
  • verify(Note $note): array

K2gl\SignedNote\SignerKey class

  • fromString(string $encoded): self
  • sign(string $text): Note

K2gl\SignedNote\VerifierKey class

  • ed25519(string $name, string $publicKey): self
  • fromString(string $encoded): self
  • fromPem(string $name, string $publicKeyPem): self
  • verify(string $signedText, string $signature): bool