Documentation ¶
Overview ¶
Package ssot implements a single source of truth (SSOT) with DNS TXT records.
Signed head specification ¶
Signed heads have the following fields:
- PUBKEY (32-byte), the Ed25519 public key of SSOT head signer.
- PUBKEY_ROTATE (32-byte), Ed25519 pubkey to rotate to, set to 0 if unused.
- VALID_FROM (8-byte), the signed head is valid from the given Unix time.
- VALID_TO (8-byte), the signed head is valid to the given Unix time.
- COUNTER (8-byte), strictly increasing signature counter.
- HEAD, the Codechain head to sign.
- SIGNATURE, signature with PUBKEY.
The SIGNATURE is over all previous fields:
PUBKEY|PUBKEY_ROTATE|VALID_FROM|VALID_TO|COUNTER|HEAD
The signed head is a concatenation of
PUBKEY|PUBKEY_ROTATE|VALID_FROM|VALID_TO|COUNTER|HEAD|SIGNATURE
encoded in base64 (URL encoding without padding).
All integers (VALID_FROM, VALID_TO, COUNTER) are encoded in network order (big-endian).
CreatePkg specification ¶
To create a new secure package for a project developed with Codechain that should be distributed with a SSOT using DNS TXT records, the following procedure is defined:
Make sure the project with NAME has not been published before. That is, the directory ~/.config/ssotpub/pkgs/NAME does not exist.
If TXT records are to be published automatically, check credentials.
Test build (see TestBuild specification).
Create a new .secpkg file which specifies the following:
- The NAME of the project. - The fully qualified domain name (DNS) where the TXT records can be queried. - The current HEAD of the project's Codechain.
The .secpkg file is saved to the current working directory, which is typically added to the root of the project's repository.
Create the first signed head (see SignHead) for the current project's HEAD with a supplied secret key and counter set to 0.
Create the directory ~/.config/ssotpub/pkgs/NAME/dists and save the current distribution to ~/.config/ssotpub/pkgs/NAME/dists/HEAD.tar.gz (`codechain createdist`).
Save the signed head to ~/.config/ssotpub/pkgs/NAME/signed_head
Print the distribution name: ~/.config/secpkg/pkgs/NAME/dists/HEAD.tar.gz
Print DNS TXT records as defined by the .secpkg, the first signed head, and the download URL. If TXT records are to be published automatically, save credentials and publish the TXT record.
Afterwards the administrator manually uploads the distribution HEAD.tar.gz to the download URL and publishes the new DNS TXT record in the defined zone (if not published automatically). DNSSEC should be enabled.
SignHead specification ¶
To publish an update of a secure package with SSOT do the following:
Parse the .secpkg file in the current working directory.
Make sure the project with NAME has been published before. That is, the directory ~/.config/ssotpub/pkgs/NAME exists.
Validate the signed head in ~/.config/ssotpub/pkgs/NAME/signed_head.
Get the HEAD from .codechain/hashchain in the current working directory.
If ~/.config/ssotpub/pkgs/NAME/cloudflare.json exits, check the contained Cloudflare credentials and switch on automatic publishing of TXT records.
Test build (see TestBuild specification).
If ROTATE is set, check if ~/.config/ssotput/pkgs/NAME/rotate_to exists. If it does, abort. Otherwise write public key to rotate to and rotate time (see below) to ~/.config/ssotput/pkgs/NAME/rotate_to.
Create a new signed head with current HEAD, the counter of the previous signed head plus 1, and update the saved signed head:
- `cp -f ~/.config/ssotpub/pkgs/NAME/signed_head ~/.config/ssotpub/pkgs/NAME/previous_signed_head` - Save new signed head to ~/.config/ssotpub/pkgs/NAME/signed_head (atomic).
If ~/.config/ssotput/pkgs/NAME/rotate_to exists:
- If rotate time has been reached use pubkey from file as PUBKEY and remove ~/.config/ssotput/pkgs/NAME/rotate_to. - Otherwise use old PUBKEY and set pubkey from file as PUBKEY_ROTATE.
If the HEAD changed, save the current distribution to: ~/.config/secpkg/pkgs/NAME/dists/HEAD.tar.gz (`codechain createdist`).
If the HEAD changed, lookup the download URLs and print where to upload the distribution file: ~/.config/ssotpkg/pkgs/NAME/dists/HEAD.tar.gz
Print DNS TXT record as defined by the .secpkg file and the signed head. If TXT records are to be published automatically, publish the TXT record.
If the HEAD changed, update the .secpkg file accordingly.
Afterwards the administrator manually uploads the distribution HEAD.tar.gz to the download URLs and publishes the new DNS TXT record in the defined zone (if not published automatically). DNSSEC should be enabled.
Refresh specification ¶
To refresh the published head of a secure package with SSOT do the following:
Parse the supplied .secpkg file.
Make sure the project with NAME has been published before. That is, the directory ~/.config/ssotpub/pkgs/NAME exists.
Validate the signed head in ~/.config/ssotpub/pkgs/NAME/signed_head.
Make sure the signed head in ~/.config/ssotpub/pkgs/NAME/signed_head matches the HEAD in the .secpkg file.
If ~/.config/ssotpub/pkgs/NAME/cloudflare.json exits, check the contained Cloudflare credentials and switch on automatic publishing of TXT records.
If ROTATE is set, check if ~/.config/ssotput/pkgs/NAME/rotate_to exists. If it does, abort. Otherwise write public key to rotate to and rotate time (see below) to ~/.config/ssotput/pkgs/NAME/rotate_to.
Create a new signed head with the same HEAD, the counter of the previous signed head plus 1, and update the saved signed head:
- `cp -f ~/.config/ssotpub/pkgs/NAME/signed_head ~/.config/ssotpub/pkgs/NAME/previous_signed_head` - Save new signed head to ~/.config/ssotpub/pkgs/NAME/signed_head (atomic).
If ~/.config/ssotput/pkgs/NAME/rotate_to exists:
- If rotate time has been reached use pubkey from file as PUBKEY and remove ~/.config/ssotput/pkgs/NAME/rotate_to. - Otherwise use old PUBKEY and set pubkey from file as PUBKEY_ROTATE.
Print DNS TXT record as defined by the .secpkg file and the signed head. If TXT record is to be published automatically, publish the TXT record.
Afterwards the administrator publishes the new DNS TXT record in the defined zone (if not published automatically). DNSSEC should be enabled.
TestBuild specification ¶
To test the build of a secure package do the following:
Create temporary directory TMPDIR with `build` and `local` subdirectories.
`mkdir TMPDIR/build/.codechain`
`cp .codechain/hashchain TMPDIR/build/.codechain`
`cp -r .codechain/patches TMPDIR/build/.codechain`
`cd TMPDIR/build`
`codechain apply`
`make prefix=TMPDIR/local`
`make prefix=TMPDIR/local install`
Make sure TMPDIR/local contains at least one file.
`make prefix=TMPDIR/local uninstall`
Make sure TMPDIR/local contains no files (but empty directories are OK).
Delete temporary directory TMPDIR.
Rotate time calculation ¶
The earliest time a PUBKEY_ROTATE can be used as PUBKEY is when the previous signed head (without PUBKEY_ROTATE) has expired. This gives clients time to learn about PUBKEY_ROTATE. To give some extra time we take the time span a signed head with PUBKEY_ROTATE is valid after the signed head without PUBKEY_ROTATE has expired and divide it by three. The rotate time is set to the end of the first third.
Index ¶
- Constants
- Variables
- func LookupURLs(ctx context.Context, dns string) ([]string, error)
- func ReadRotateTo(filename string) (string, bool, error)
- func TXTPrintURL(dns, url string)
- type SignedHead
- func (sh *SignedHead) Counter() uint64
- func (sh *SignedHead) Head() string
- func (sh *SignedHead) HeadBuf() [32]byte
- func (sh *SignedHead) Marshal() string
- func (sh *SignedHead) MarshalText() string
- func (sh *SignedHead) PubKey() string
- func (sh *SignedHead) PubKeyRotate() string
- func (sh *SignedHead) RotateFile(pkgDir string) error
- func (sh *SignedHead) TXTPrintHead(dns string)
- func (sh *SignedHead) Valid() error
- func (sh *SignedHead) WriteRotateTo(filename string, secKeyRotate *[64]byte, sigRotate *[64]byte, ...) error
Constants ¶
const File = "signed_head"
File defines the default file name for a signed head.
const MaximumValidity = 30 * 24 * time.Hour // 30d
MaximumValidity of signed heads.
const MinimumValidity = 1 * time.Hour // 1h
MinimumValidity of signed heads.
const TTL = 600 // 10m
TTL of signed head TXT records
Variables ¶
var ErrSignedHeadExpired = errors.New("ssot: signed head is expired")
ErrSignedHeadExpired is returned if the validity of a signed head is expired.
var ErrSignedHeadFuture = errors.New("ssot: signed head is valid in the future")
ErrSignedHeadFuture is returned if the validity of a signed head is in the future.
var ErrSignedHeadSignature = errors.New("ssot: signed head signature does not verify")
ErrSignedHeadSignature is returned if a signed head signature does not verify.
var ErrValidityTooLong = errors.New("ssot: validity is too long")
ErrValidityTooLong is returned if the validity is too long.
var ErrValidityTooShort = errors.New("ssot: validity is too short")
ErrValidityTooShort is returned if the validity is too short.
Functions ¶
func LookupURLs ¶ added in v1.1.0
LookupURLs looks up all URLs from dns and returns it.
func ReadRotateTo ¶
ReadRotateTo reads "rotate to" file from given filename and returns the public key to rotate to and a bool indicating if the rotation time has been reached.
func TXTPrintURL ¶
func TXTPrintURL(dns, url string)
TXTPrintURL prints the TXT record to publish the url.
Types ¶
type SignedHead ¶
type SignedHead struct {
// contains filtered or unexported fields
}
SignedHead is a signed Codechain head ready for publication as a SSOT with DNS TXT records.
func Load ¶
func Load(filename string) (*SignedHead, error)
Load and verify a base64 encoded signed head from filename.
func LookupHead ¶
func LookupHead(ctx context.Context, dns string) (*SignedHead, error)
LookupHead and verify base64 encoded signed head from dns.
func SignHead ¶
func SignHead( head [32]byte, counter uint64, secKey [64]byte, pubKeyRotate *[32]byte, validity time.Duration, ) (*SignedHead, error)
SignHead signs the given Codechain head.
func Unmarshal ¶
func Unmarshal(signedHead string) (*SignedHead, error)
Unmarshal and verify a base64 encoded signed head.
func (*SignedHead) Counter ¶
func (sh *SignedHead) Counter() uint64
Counter returns the counter of signed head.
func (*SignedHead) HeadBuf ¶
func (sh *SignedHead) HeadBuf() [32]byte
HeadBuf returns the signed head.
func (*SignedHead) Marshal ¶
func (sh *SignedHead) Marshal() string
Marshal signed head with signature and encode it as base64.
func (*SignedHead) MarshalText ¶
func (sh *SignedHead) MarshalText() string
MarshalText marshals signed head as text (for status output).
func (*SignedHead) PubKey ¶
func (sh *SignedHead) PubKey() string
PubKey returns the public key in base64 notation.
func (*SignedHead) PubKeyRotate ¶
func (sh *SignedHead) PubKeyRotate() string
PubKeyRotate returns the public key rotate in base64 notation.
func (*SignedHead) RotateFile ¶
func (sh *SignedHead) RotateFile(pkgDir string) error
RotateFile rotates the pkgDir/signed_head to pkgDir/previous_signed_head and saves signed head sh to pkgDir/signed_head.
func (*SignedHead) TXTPrintHead ¶
func (sh *SignedHead) TXTPrintHead(dns string)
TXTPrintHead prints the TXT record to publish the signed head.
func (*SignedHead) Valid ¶
func (sh *SignedHead) Valid() error
Valid checks if the signed head sh is currently valid (as defined by validFrom and validTo). It returns nil, if the signed check is valid and an error otherwise.
func (*SignedHead) WriteRotateTo ¶
func (sh *SignedHead) WriteRotateTo( filename string, secKeyRotate *[64]byte, sigRotate *[64]byte, commentRotate []byte, validity time.Duration, ) error
WriteRotateTo writes "rotate to" file to given filename.