Documentation ¶
Overview ¶
Package note defines the notes signed by the Go module database server.
A note is text signed by one or more server keys. The text should be ignored unless the note is signed by a trusted server key and the signature has been verified using the server's public key.
A server's public key is identified by a name, typically the "host[/path]" giving the base URL of the server's transparency log. The syntactic restrictions on a name are that it be non-empty, well-formed UTF-8 containing neither Unicode spaces nor plus (U+002B).
A Go module database server signs texts using public key cryptography. A given server may have multiple public keys, each identified by the first 32 bits of the SHA-256 hash of the concatenation of the server name, a newline, and the encoded public key.
Verifying Notes ¶
A Verifier allows verification of signatures by one server public key. It can report the name of the server and the uint32 hash of the key, and it can verify a purported signature by that key.
The standard implementation of a Verifier is constructed by NewVerifier starting from a verifier key, which is a plain text string of the form "<name>+<hash>+<keydata>".
A Verifiers allows looking up a Verifier by the combination of server name and key hash.
The standard implementation of a Verifiers is constructed by VerifierList from a list of known verifiers.
A Note represents a text with one or more signatures. An implementation can reject a note with too many signatures (for example, more than 100 signatures).
A Signature represents a signature on a note, verified or not.
The Open function takes as input a signed message and a set of known verifiers. It decodes and verifies the message signatures and returns a Note structure containing the message text and (verified or unverified) signatures.
Signing Notes ¶
A Signer allows signing a text with a given key. It can report the name of the server and the hash of the key and can sign a raw text using that key.
The standard implementation of a Signer is constructed by NewSigner starting from an encoded signer key, which is a plain text string of the form "PRIVATE+KEY+<name>+<hash>+<keydata>". Anyone with an encoded signer key can sign messages using that key, so it must be kept secret. The encoding begins with the literal text "PRIVATE+KEY" to avoid confusion with the public server key.
The Sign function takes as input a Note and a list of Signers and returns an encoded, signed message.
Signed Note Format ¶
A signed note consists of a text ending in newline (U+000A), followed by a blank line (only a newline), followed by one or more signature lines of this form: em dash (U+2014), space (U+0020), server name, space, base64-encoded signature, newline.
Signed notes must be valid UTF-8 and must not contain any ASCII control characters (those below U+0020) other than newline.
A signature is a base64 encoding of 4+n bytes.
The first four bytes in the signature are the uint32 key hash stored in big-endian order, which is to say they are the first four bytes of the truncated SHA-256 used to derive the key hash in the first place.
The remaining n bytes are the result of using the specified key to sign the note text (including the final newline but not the separating blank line).
Generating Keys ¶
There is only one key type, Ed25519 with algorithm identifier 1. New key types may be introduced in the future as needed, although doing so will require deploying the new algorithms to all clients before starting to depend on them for signatures.
The GenerateKey function generates and returns a new signer and corresponding verifier.
Example ¶
Here is a well-formed signed note:
If you think cryptography is the answer to your problem, then you don't know what your problem is. — PeterNeumann x08go/ZJkuBS9UG/SffcvIAQxVBtiFupLLr8pAcElZInNIuGUgYN1FFYC2pZSNXgKvqfqdngotpRZb6KE6RyyBwJnAM=
It can be constructed and displayed using:
skey := "PRIVATE+KEY+PeterNeumann+c74f20a3+AYEKFALVFGyNhPJEMzD1QIDr+Y7hfZx09iUvxdXHKDFz" text := "If you think cryptography is the answer to your problem,\n" + "then you don't know what your problem is.\n" signer, err := note.NewSigner(skey) if err != nil { log.Fatal(err) } msg, err := note.Sign(¬e.Note{Text: text}, signer) if err != nil { log.Fatal(err) } os.Stdout.Write(msg)
The note's text is two lines, including the final newline, and the text is purportedly signed by a server named "PeterNeumann". (Although server names are canonically base URLs, the only syntactic requirement is that they not contain spaces or newlines).
If Open is given access to a Verifiers including the Verifier for this key, then it will succeed at verifiying the encoded message and returning the parsed Note:
vkey := "PeterNeumann+c74f20a3+ARpc2QcUPDhMQegwxbzhKqiBfsVkmqq/LDE4izWy10TW" msg := []byte("If you think cryptography is the answer to your problem,\n" + "then you don't know what your problem is.\n" + "\n" + "— PeterNeumann x08go/ZJkuBS9UG/SffcvIAQxVBtiFupLLr8pAcElZInNIuGUgYN1FFYC2pZSNXgKvqfqdngotpRZb6KE6RyyBwJnAM=\n") verifier, err := note.NewVerifier(vkey) if err != nil { log.Fatal(err) } verifiers := note.VerifierList(verifier) n, err := note.Open([]byte(msg), verifiers) if err != nil { log.Fatal(err) } fmt.Printf("%s (%08x):\n%s", n.Sigs[0].Name, n.Sigs[0].Hash, n.Text)
You can add your own signature to this message by re-signing the note:
skey, vkey, err := note.GenerateKey(rand.Reader, "EnochRoot") if err != nil { log.Fatal(err) } _ = vkey // give to verifiers me, err := note.NewSigner(skey) if err != nil { log.Fatal(err) } msg, err := note.Sign(n, me) if err != nil { log.Fatal(err) } os.Stdout.Write(msg)
This will print a doubly-signed message, like:
If you think cryptography is the answer to your problem, then you don't know what your problem is. — PeterNeumann x08go/ZJkuBS9UG/SffcvIAQxVBtiFupLLr8pAcElZInNIuGUgYN1FFYC2pZSNXgKvqfqdngotpRZb6KE6RyyBwJnAM= — EnochRoot rwz+eBzmZa0SO3NbfRGzPCpDckykFXSdeX+MNtCOXm2/5n2tiOHp+vAF1aGrQ5ovTG01oOTGwnWLox33WWd1RvMc+QQ=
Index ¶
- func GenerateKey(rand io.Reader, name string) (skey, vkey string, err error)
- func NewEd25519VerifierKey(name string, key ed25519.PublicKey) (string, error)
- func Sign(n *Note, signers ...Signer) ([]byte, error)
- type InvalidSignatureError
- type Note
- type Signature
- type Signer
- type UnknownVerifierError
- type UnverifiedNoteError
- type Verifier
- type Verifiers
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func GenerateKey ¶
GenerateKey generates a signer and verifier key pair for a named server. The signer key skey is private and must be kept secret.
func NewEd25519VerifierKey ¶
NewEd25519VerifierKey returns an encoded verifier key using the given name and Ed25519 public key.
func Sign ¶
Sign signs the note with the given signers and returns the encoded message. The new signatures from signers are listed in the encoded message after the existing signatures already present in n.Sigs. If any signer uses the same key as an existing signature, the existing signature is elided from the output.
Types ¶
type InvalidSignatureError ¶
An InvalidSignatureError indicates that the given key was known and the associated Verifier rejected the signature.
func (*InvalidSignatureError) Error ¶
func (e *InvalidSignatureError) Error() string
type Note ¶
type Note struct { Text string // text of note Sigs []Signature // verified signatures UnverifiedSigs []Signature // unverified signatures }
A Note is a text and signatures.
func Open ¶
Open opens and parses the message msg, checking signatures from the known verifiers.
For each signature in the message, Open calls known.Verifier to find a verifier. If known.Verifier returns a verifier and the verifier accepts the signature, Open records the signature in the returned note's Sigs field. If known.Verifier returns a verifier but the verifier rejects the signature, Open returns an InvalidSignatureError. If known.Verifier returns an UnknownVerifierError, Open records the signature in the returned note's UnverifiedSigs field. If known.Verifier returns any other error, Open returns that error.
If no known verifier has signed an otherwise valid note, Open returns an UnverifiedNoteError. In this case, the unverified note can be fetched from inside the error.
type Signature ¶
type Signature struct { // Name and Hash give the name and key hash // for the key that generated the signature. Name string Hash uint32 // Base64 records the base64-encoded signature bytes. Base64 string }
A Signature is a single signature found in a note.
type Signer ¶
type Signer interface { // Name returns the server name associated with the key. Name() string // KeyHash returns the key hash. KeyHash() uint32 // Sign returns a signature for the given message. Sign(msg []byte) ([]byte, error) }
A Signer signs messages using a specific key.
type UnknownVerifierError ¶
An UnknownVerifierError indicates that the given key is not known. The Open function records signatures without associated verifiers as unverified signatures.
func (*UnknownVerifierError) Error ¶
func (e *UnknownVerifierError) Error() string
type UnverifiedNoteError ¶
type UnverifiedNoteError struct {
Note *Note
}
An UnverifiedNoteError indicates that the note successfully parsed but had no verifiable signatures.
func (*UnverifiedNoteError) Error ¶
func (e *UnverifiedNoteError) Error() string
type Verifier ¶
type Verifier interface { // Name returns the server name associated with the key. Name() string // KeyHash returns the key hash. KeyHash() uint32 // Verify reports whether sig is a valid signature of msg. Verify(msg, sig []byte) bool }
A Verifier verifies messages signed with a specific key.
func NewVerifier ¶
NewVerifier construct a new Verifier from an encoded verifier key.
type Verifiers ¶
type Verifiers interface { // Verifier returns the Verifier associated with the key // identified by the name and hash. // If the name, hash pair is unknown, Verifier should return // an UnknownVerifierError. Verifier(name string, hash uint32) (Verifier, error) }
A Verifiers is a collection of known verifier keys.
func VerifierList ¶
VerifierList returns a Verifiers implementation that uses the given list of verifiers.