detached cab7cdc

signing.go RawHistoryFolder

1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package kvstore
4
5import (
6	"context"
7	"errors"
8	"fmt"
9	"time"
10
11	kv "bnema.dev/ermite-kv"
12	"google.golang.org/protobuf/proto"
13
14	storagepb "bnema.dev/ermite/gen/ermite/storage"
15)
16
17const signingKeyKey = "signing/key"
18
19// SigningSeedLen is the length of the stored seed of the signing key.
20const SigningSeedLen = 32
21
22// SigningSeed returns the 32-byte seed of the commit signing key (ADR 0022).
23// When none is stored, it calls generate, stores what it returns and returns
24// that; of two concurrent first calls one wins and both return its seed. The
25// seed is the private key: neither it nor an error carrying it is logged.
26func (s *Store) SigningSeed(ctx context.Context, generate func() ([]byte, error), now time.Time) ([]byte, error) {
27	key := []byte(signingKeyKey)
28	for range 2 {
29		if item, ok := s.db.Get(ctx, key); ok {
30			return decodeSigningSeed(item.Value)
31		}
32		seed, err := generate()
33		if err != nil {
34			return nil, fmt.Errorf("generate the signing key: %w", err)
35		}
36		if len(seed) != SigningSeedLen {
37			return nil, fmt.Errorf("generate the signing key: seed has %d bytes, want %d", len(seed), SigningSeedLen)
38		}
39		var b kv.Batch
40		b.RequireAbsent(key)
41		b.Put(key, mustMarshal(&storagepb.SigningKey{Seed: seed, CreatedAt: encodeTime(now)}))
42		_, err = s.db.Apply(ctx, &b)
43		if errors.Is(err, kv.ErrConflict) {
44			continue // written meanwhile: read it
45		}
46		if err != nil {
47			return nil, fmt.Errorf("store the signing key: %w", err)
48		}
49		return seed, nil
50	}
51	return nil, errors.New("store the signing key: concurrent initialization did not settle")
52}
53
54func decodeSigningSeed(val []byte) ([]byte, error) {
55	var pb storagepb.SigningKey
56	if err := proto.Unmarshal(val, &pb); err != nil {
57		return nil, fmt.Errorf("decode the signing key: %w", err)
58	}
59	if len(pb.GetSeed()) != SigningSeedLen {
60		return nil, fmt.Errorf("decode the signing key: seed has %d bytes, want %d", len(pb.GetSeed()), SigningSeedLen)
61	}
62	return pb.GetSeed(), nil
63}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 4

Tags 45

↑↓ moveenter openesc close