├── pkg ├── event │ ├── message_test.go │ ├── seal_test.go │ ├── message.go │ ├── attachment.go │ ├── attached-receipt.go │ ├── receipt_test.go │ ├── seal.go │ ├── main_test.go │ ├── receipt.go │ ├── event_test.go │ ├── sig-threshold.go │ ├── main.go │ └── sig-threshold_test.go ├── version │ └── main.go ├── derivation │ ├── basic.go │ ├── self-signing.go │ ├── ordinal_test.go │ ├── ordinal.go │ ├── self-addressing.go │ ├── counter_test.go │ ├── counter.go │ ├── code.go │ ├── attached-signature.go │ └── main.go ├── encoding │ ├── reader.go │ ├── writer.go │ └── stream │ │ ├── detect.go │ │ ├── writer.go │ │ ├── stream_test.go │ │ ├── reader.go │ │ └── serialize.go ├── db │ ├── badger │ │ ├── logger.go │ │ ├── value_test.go │ │ ├── set.go │ │ ├── set_test.go │ │ ├── ordered_set.go │ │ ├── ordered_set_test.go │ │ ├── iterator.go │ │ ├── value.go │ │ └── vals.go │ ├── db.go │ └── mem │ │ ├── mem.go │ │ └── mem_test.go ├── test │ ├── util │ │ └── util.go │ ├── kms │ │ └── kms.go │ └── inception.go ├── prefix │ ├── base.go │ ├── base_test.go │ └── main.go ├── keri │ ├── replay.go │ ├── receipts.go │ └── keri_test.go ├── direct │ ├── connection.go │ ├── client.go │ ├── server.go │ └── direct_test.go ├── main_test.go ├── log │ ├── escrow.go │ └── escrow_test.go └── keymanager │ ├── keymanager_test.go │ └── keymanager.go ├── .github └── workflows │ ├── interop │ └── Dockerfile │ └── build.yaml ├── .gitignore ├── scripts └── check_unit.sh ├── interop └── README.md ├── go.mod ├── Makefile ├── README.md └── cmd └── demo ├── eve └── eve.go ├── sam └── sam.go ├── bob └── bob.go └── joe └── main.go /pkg/event/message_test.go: -------------------------------------------------------------------------------- 1 | package event 2 | -------------------------------------------------------------------------------- /pkg/version/main.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import "fmt" 4 | 5 | func Code() string { 6 | return fmt.Sprintf("%x%x", 1, 0) 7 | } 8 | -------------------------------------------------------------------------------- /pkg/derivation/basic.go: -------------------------------------------------------------------------------- 1 | package derivation 2 | 3 | // Basic derivations are just byte representations of data. 4 | func basicDeriver() deriver { 5 | return func(data []byte) ([]byte, error) { 6 | return data, nil 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /pkg/encoding/reader.go: -------------------------------------------------------------------------------- 1 | package encoding 2 | 3 | import ( 4 | "github.com/decentralized-identity/kerigo/pkg/event" 5 | ) 6 | 7 | type Reader interface { 8 | Read() (*event.Message, error) 9 | ReadAll() ([]*event.Message, error) 10 | } 11 | -------------------------------------------------------------------------------- /pkg/encoding/writer.go: -------------------------------------------------------------------------------- 1 | package encoding 2 | 3 | import ( 4 | "github.com/decentralized-identity/kerigo/pkg/event" 5 | ) 6 | 7 | type Writer interface { 8 | Write(msg *event.Message) error 9 | WriteAll(msg []*event.Message) error 10 | } 11 | -------------------------------------------------------------------------------- /.github/workflows/interop/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.15.8-buster 2 | 3 | RUN git clone https://github.com/decentralized-identity/kerigo.git /root/kerigo 4 | 5 | WORKDIR /root/kerigo 6 | 7 | RUN go build -o bob cmd/demo/bob/bob.go 8 | RUN go build -o eve cmd/demo/eve/eve.go 9 | RUN go build -o sam cmd/demo/sam/sam.go 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | /.idea/ 17 | ./bin 18 | -------------------------------------------------------------------------------- /pkg/db/badger/logger.go: -------------------------------------------------------------------------------- 1 | package badger 2 | 3 | type NoOpLogger struct { 4 | } 5 | 6 | func (r *NoOpLogger) Errorf(s string, i ...interface{}) { 7 | } 8 | 9 | func (r *NoOpLogger) Warningf(s string, i ...interface{}) { 10 | } 11 | 12 | func (r *NoOpLogger) Infof(s string, i ...interface{}) { 13 | } 14 | 15 | func (r *NoOpLogger) Debugf(s string, i ...interface{}) { 16 | } 17 | -------------------------------------------------------------------------------- /scripts/check_unit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | echo -n > coverage.out 5 | echo "Running $0" 6 | 7 | GO_TEST_CMD="go test" 8 | 9 | PKGS=$(go list github.com/decentralized-identity/kerigo/... 2> /dev/null | grep -v vendor) 10 | $GO_TEST_CMD $PKGS -count=1 -race -coverprofile=profile.out -covermode=atomic -timeout=10m 11 | if [ -f profile.out ]; then 12 | cat profile.out >>coverage.out 13 | rm profile.out 14 | fi 15 | -------------------------------------------------------------------------------- /pkg/test/util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func GetTempDir(t *testing.T) (string, func()) { 12 | td, err := ioutil.TempDir("", "badger-test-*") 13 | require.NoError(t, err) 14 | 15 | cleanup := func() { 16 | err := os.RemoveAll(td) 17 | require.NoError(t, err) 18 | } 19 | 20 | return td, cleanup 21 | } 22 | -------------------------------------------------------------------------------- /interop/README.md: -------------------------------------------------------------------------------- 1 | ### Interop 2 | 3 | Demos are built on new pushes to `master`, they can be used to test integration with other core KERI libraries 4 | * [keripy](https://github.com/decentralized-identity/keripy) 5 | * [kerigo](https://github.com/decentralized-identity/kerigo) 6 | 7 | #### Running demos 8 | 9 | ##### Bob 10 | ```shell 11 | make demo-bob 12 | ``` 13 | 14 | ##### Eve 15 | ```shell 16 | make demo-eve 17 | ``` 18 | 19 | ##### Sam 20 | ```shell 21 | make demo-sam 22 | ``` 23 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/decentralized-identity/kerigo 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/cenkalti/backoff/v4 v4.1.0 7 | github.com/dgraph-io/badger v1.6.2 8 | github.com/google/tink/go v1.5.0 9 | github.com/mitchellh/mapstructure v1.1.2 10 | github.com/pkg/errors v0.9.1 11 | github.com/stretchr/testify v1.6.1 12 | github.com/ugorji/go v1.2.4 // indirect 13 | github.com/ugorji/go/codec v1.2.4 14 | golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 15 | golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 // indirect 16 | lukechampine.com/blake3 v1.1.4 17 | ) 18 | -------------------------------------------------------------------------------- /pkg/prefix/base.go: -------------------------------------------------------------------------------- 1 | package prefix 2 | 3 | import ( 4 | "encoding/base64" 5 | 6 | "github.com/decentralized-identity/kerigo/pkg/derivation" 7 | ) 8 | 9 | type base struct { 10 | derivation *derivation.Derivation // the derivation 11 | } 12 | 13 | func (b *base) String() string { 14 | return string(append([]byte(b.derivation.Code.String()), base64.RawURLEncoding.EncodeToString(b.derivation.Raw)...)) 15 | } 16 | 17 | func (b *base) Derivation() *derivation.Derivation { 18 | return b.derivation 19 | } 20 | 21 | func (b *base) Raw() []byte { 22 | return b.derivation.Raw 23 | } 24 | -------------------------------------------------------------------------------- /pkg/derivation/self-signing.go: -------------------------------------------------------------------------------- 1 | package derivation 2 | 3 | import "errors" 4 | 5 | // Signer function for signing self-signing derivations 6 | // KERI does not want to access any private key data directly. 7 | // This function can take the provided input bytes, sign it using 8 | // the appropriate key and return the signed data. 9 | // The Signer function has the same signature as, and will be 10 | // used in place of, the deriver 11 | type Signer func(raw []byte) ([]byte, error) 12 | 13 | // Self signing derivations must provide a signer function 14 | func selfSigningDeriver(c Code) (d deriver) { 15 | return func(data []byte) ([]byte, error) { 16 | return nil, errors.New("For self-signing derivations must provide Signer function") 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /pkg/test/kms/kms.go: -------------------------------------------------------------------------------- 1 | package kms 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/google/tink/go/aead" 7 | "github.com/google/tink/go/keyset" 8 | "github.com/stretchr/testify/assert" 9 | 10 | "github.com/decentralized-identity/kerigo/pkg/db" 11 | "github.com/decentralized-identity/kerigo/pkg/keymanager" 12 | ) 13 | 14 | func GetKMS(t *testing.T, secrets []string, store db.DB) *keymanager.KeyManager { 15 | 16 | kh, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) 17 | assert.NoError(t, err) 18 | 19 | a, err := aead.New(kh) 20 | assert.NoError(t, err) 21 | 22 | km, err := keymanager.NewKeyManager(keymanager.WithAEAD(a), keymanager.WithSecrets(secrets), keymanager.WithStore(store)) 23 | assert.NoError(t, err) 24 | 25 | return km 26 | } 27 | -------------------------------------------------------------------------------- /pkg/keri/replay.go: -------------------------------------------------------------------------------- 1 | package keri 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | 6 | "github.com/decentralized-identity/kerigo/pkg/event" 7 | ) 8 | 9 | type ReplayMode int 10 | 11 | const ( 12 | FirstSeenReplay ReplayMode = iota 13 | SequenceNumberReplay 14 | ) 15 | 16 | type ReplayHandler func(e *event.Message) error 17 | 18 | func (r *Keri) Replay(pre string, mode ReplayMode, handler ReplayHandler) error { 19 | var streamer func(string, func(e *event.Message) error) error 20 | switch mode { 21 | case FirstSeenReplay: 22 | streamer = r.db.StreamAsFirstSeen 23 | case SequenceNumberReplay: 24 | streamer = r.db.StreamBySequenceNo 25 | default: 26 | return errors.New("invalid replay type") 27 | } 28 | 29 | return streamer(pre, handler) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/db/badger/value_test.go: -------------------------------------------------------------------------------- 1 | package badger 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | "time" 7 | 8 | "github.com/dgraph-io/badger" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestValue_Last(t *testing.T) { 13 | td, cleanup := getTempDir(t) 14 | defer cleanup() 15 | 16 | db, err := badger.Open(badger.DefaultOptions(td)) 17 | assert.NoError(t, err) 18 | 19 | txn := db.NewTransaction(true) 20 | 21 | vals := NewValue("fses", "/%s/%s.%08d") 22 | 23 | for i := 0; i < 10; i++ { 24 | d := strconv.Itoa(i) 25 | now := time.Now() 26 | dts := now.Format(time.RFC3339) 27 | 28 | err := vals.Set(txn, []byte(d), "pre", dts, now.Nanosecond()) 29 | assert.NoError(t, err) 30 | } 31 | 32 | last, err := vals.Last(txn, "pre") 33 | assert.NoError(t, err) 34 | assert.Equal(t, "9", string(last)) 35 | first, err := vals.First(txn, "pre") 36 | assert.NoError(t, err) 37 | assert.Equal(t, "0", string(first)) 38 | } 39 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: test 3 | 4 | .PHONY: test 5 | test: 6 | @scripts/check_unit.sh 7 | 8 | .PHONY: demo-bob 9 | demo-bob: 10 | @go build -o ./bin/bob cmd/demo/bob/bob.go 11 | @./bin/bob -e 10 12 | 13 | .PHONY: demo-eve 14 | demo-eve: 15 | @go build -o ./bin/eve cmd/demo/eve/eve.go 16 | @./bin/eve -e 10 17 | 18 | .PHONY: demo-sam 19 | demo-sam: 20 | @go build -o ./bin/sam cmd/demo/sam/sam.go 21 | @./bin/sam -e 10 22 | 23 | .PHONY: interop-bob 24 | interop-bob: 25 | @docker run --rm -i -p 5620-5621 --name kerigo-bob ghcr.io/decentralized-identity/kerigo/kerigo-interop bash -c './bob -e 10' 26 | 27 | .PHONY: interop-eve 28 | interop-eve: 29 | @docker run --rm -i -p 5620-5621 --name kerigo-eve ghcr.io/decentralized-identity/kerigo/kerigo-interop bash -c './eve -e 10' 30 | 31 | .PHONY: interop-sam 32 | interop-sam: 33 | @docker run --rm -i -p 5620-5621 --name kerigo-sam ghcr.io/decentralized-identity/kerigo/kerigo-interop bash -c './sam -e 10' 34 | 35 | -------------------------------------------------------------------------------- /pkg/derivation/ordinal_test.go: -------------------------------------------------------------------------------- 1 | package derivation 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNewOrdinal(t *testing.T) { 11 | type args struct { 12 | val int 13 | } 14 | 15 | tests := []struct { 16 | name string 17 | args args 18 | want []byte 19 | }{ 20 | { 21 | name: "4", 22 | args: args{4}, 23 | want: []byte("0AAAAAAAAAAAAAAAAAAAAABA"), 24 | }, 25 | { 26 | name: "12", 27 | args: args{12}, 28 | want: []byte("0AAAAAAAAAAAAAAAAAAAAADA"), 29 | }, 30 | { 31 | name: "256", 32 | args: args{256}, 33 | want: []byte("0AAAAAAAAAAAAAAAAAAAABAA"), 34 | }, 35 | } 36 | for _, tt := range tests { 37 | t.Run(tt.name, func(t *testing.T) { 38 | o := NewOrdinal(uint16(tt.args.val)) 39 | b64 := o.Base64() 40 | 41 | assert.Equal(t, b64, tt.want) 42 | 43 | r := bytes.NewReader(b64) 44 | 45 | o, err := ParseOrdinal(r) 46 | assert.NoError(t, err) 47 | assert.Equal(t, tt.args.val, o.Num()) 48 | }) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /pkg/encoding/stream/detect.go: -------------------------------------------------------------------------------- 1 | package stream 2 | 3 | type FrameType int 4 | 5 | const ( 6 | FrameMask = 0b11100000 7 | 8 | CBORFrame FrameType = iota 9 | MsgPackFrame 10 | JSONFrame 11 | QB64Frame 12 | QB2Frame 13 | 14 | CBORPrefix = byte(0b10100000) 15 | MsgPackPrefix = byte(0b10000000) 16 | JSONPrefix = byte(0b01100000) 17 | QB64Prefix = byte(0b00100000) 18 | QB2Prefix = byte(0b01000000) 19 | ) 20 | 21 | var ( 22 | FrameTypePrefixes = map[FrameType]byte{ 23 | CBORFrame: CBORPrefix, 24 | MsgPackFrame: MsgPackPrefix, 25 | JSONFrame: JSONPrefix, 26 | QB64Frame: QB64Prefix, 27 | QB2Frame: QB2Prefix, 28 | } 29 | 30 | FrameTypes = map[byte]FrameType{ 31 | CBORPrefix: CBORFrame, 32 | MsgPackPrefix: MsgPackFrame, 33 | JSONPrefix: JSONFrame, 34 | QB64Prefix: QB64Frame, 35 | QB2Prefix: QB2Frame, 36 | } 37 | 38 | frameTypeNames = map[FrameType]string{ 39 | CBORFrame: "CBOR", 40 | MsgPackFrame: "MsgPack", 41 | JSONFrame: "JSON", 42 | QB64Frame: "QB64", 43 | QB2Frame: "QB2", 44 | } 45 | ) 46 | 47 | func DetectFrameType(d []byte) FrameType { 48 | m := d[0] & FrameMask 49 | frameType, ok := FrameTypes[m] 50 | if !ok { 51 | return -1 52 | } 53 | 54 | return frameType 55 | } 56 | -------------------------------------------------------------------------------- /pkg/direct/connection.go: -------------------------------------------------------------------------------- 1 | package direct 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "net" 7 | 8 | "github.com/pkg/errors" 9 | 10 | "github.com/decentralized-identity/kerigo/pkg/encoding" 11 | "github.com/decentralized-identity/kerigo/pkg/event" 12 | "github.com/decentralized-identity/kerigo/pkg/keri" 13 | ) 14 | 15 | type conn struct { 16 | reader encoding.Reader 17 | conn net.Conn 18 | writer encoding.Writer 19 | } 20 | 21 | func (r *conn) Write(msg *event.Message) error { 22 | err := r.writer.Write(msg) 23 | if err != nil { 24 | return errors.Wrap(err, "unable to right message to stream outbound") 25 | } 26 | 27 | b, _ := json.Marshal(msg) 28 | log.Print(msg.Event.Prefix, "sent event:\n", string(b), "\n\n") 29 | 30 | return nil 31 | } 32 | 33 | func handleConnection(ioc *conn, id *keri.Keri) error { 34 | 35 | for { 36 | msg, err := ioc.reader.Read() 37 | if err != nil { 38 | return err 39 | } 40 | 41 | outmsgs, err := id.ProcessEvents(msg) 42 | if err != nil { 43 | return errors.Wrap(err, "error reading message on connection") 44 | } 45 | 46 | for _, msg := range outmsgs { 47 | err := ioc.Write(msg) 48 | if err != nil { 49 | return errors.Wrap(err, "error writing initial message to connection") 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /pkg/keri/receipts.go: -------------------------------------------------------------------------------- 1 | package keri 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/pkg/errors" 7 | 8 | "github.com/decentralized-identity/kerigo/pkg/event" 9 | ) 10 | 11 | // Message thread-safe message register structure. 12 | type Receipts struct { 13 | mu sync.RWMutex 14 | events []chan<- *event.Event 15 | } 16 | 17 | // RcptChans returns receipt channels. 18 | func (r *Receipts) RcptChans() []chan<- *event.Event { 19 | r.mu.RLock() 20 | events := append(r.events[:0:0], r.events...) 21 | r.mu.RUnlock() 22 | 23 | return events 24 | } 25 | 26 | // RegisterMsgEvent on protocol messages. The message events are triggered for incoming messages. Event 27 | // will not expect any callback on these events unlike Action events. 28 | func (r *Receipts) RegisterRcptChan(ch chan<- *event.Event) error { 29 | if ch == nil { 30 | return errors.New("nil channel") 31 | } 32 | 33 | r.mu.Lock() 34 | r.events = append(r.events, ch) 35 | r.mu.Unlock() 36 | 37 | return nil 38 | } 39 | 40 | // UnregisterMsgEvent on protocol messages. Refer RegisterMsgEvent(). 41 | func (r *Receipts) UnregisterRcptChan(ch chan<- *event.Event) error { 42 | r.mu.Lock() 43 | for i := 0; i < len(r.events); i++ { 44 | if r.events[i] == ch { 45 | r.events = append(r.events[:i], r.events[i+1:]...) 46 | i-- 47 | } 48 | } 49 | r.mu.Unlock() 50 | 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /pkg/encoding/stream/writer.go: -------------------------------------------------------------------------------- 1 | package stream 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | 8 | "github.com/decentralized-identity/kerigo/pkg/event" 9 | ) 10 | 11 | type Writer struct { 12 | writ io.Writer 13 | mode ReplayMode 14 | } 15 | 16 | type EncodeOption func(*Writer) 17 | 18 | func NewWriter(w io.Writer, opts ...EncodeOption) *Writer { 19 | enc := &Writer{ 20 | writ: w, 21 | mode: DisjointMode, 22 | } 23 | 24 | for _, opt := range opts { 25 | opt(enc) 26 | } 27 | 28 | return enc 29 | } 30 | 31 | func (r *Writer) WriteAll(msgs []*event.Message) error { 32 | 33 | for _, msg := range msgs { 34 | err := r.Write(msg) 35 | if err != nil { 36 | return err 37 | } 38 | } 39 | 40 | return nil 41 | } 42 | 43 | func (r *Writer) Write(msg *event.Message) error { 44 | var err error 45 | var d []byte 46 | 47 | switch r.mode { 48 | case DisjointMode: 49 | d, err = ToDisjoint(msg) 50 | case ConjointMode: 51 | d, err = ToConjoint(msg) 52 | default: 53 | return errors.New("invalid stream mode") 54 | } 55 | 56 | if err != nil { 57 | return err 58 | } 59 | 60 | _, err = r.writ.Write(d) 61 | if err != nil { 62 | return fmt.Errorf("error writing message (%v)", err) 63 | } 64 | 65 | return nil 66 | } 67 | 68 | func WithSerializationMode(sm ReplayMode) EncodeOption { 69 | return func(e *Writer) { 70 | e.mode = sm 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /pkg/db/db.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "github.com/decentralized-identity/kerigo/pkg/event" 5 | ) 6 | 7 | type DB interface { 8 | Put(k string, v []byte) error 9 | Get(k string) ([]byte, error) 10 | 11 | LogEvent(e *event.Message, first bool) error 12 | LogTransferableReceipt(vrc *event.Receipt) error 13 | LogNonTransferableReceipt(rct *event.Receipt) error 14 | 15 | EscrowPendingEvent(e *event.Message) error 16 | RemovePendingEscrow(prefix string, sn int, dig string) error 17 | 18 | EscrowOutOfOrderEvent(e *event.Message) error 19 | EscrowLikelyDuplicitiousEvent(e *event.Message) error 20 | 21 | LogSize(pre string) int 22 | StreamEstablisment(pre string, handler func(*event.Message) error) error 23 | StreamAsFirstSeen(pre string, handler func(*event.Message) error) error 24 | StreamBySequenceNo(pre string, handler func(*event.Message) error) error 25 | StreamPending(pre string, handler func(*event.Message) error) error 26 | StreamTransferableReceipts(pre string, sn int, handler func(quadlet []byte) error) error 27 | 28 | Seen(pre string) bool 29 | Inception(pre string) (*event.Message, error) 30 | CurrentEvent(pre string) (*event.Message, error) 31 | CurrentEstablishmentEvent(pre string) (*event.Message, error) 32 | EventAt(prefix string, sequence int) (*event.Message, error) 33 | 34 | LastAcceptedDigest(pre string, seq int) ([]byte, error) 35 | 36 | Close() error 37 | } 38 | -------------------------------------------------------------------------------- /pkg/prefix/base_test.go: -------------------------------------------------------------------------------- 1 | package prefix 2 | 3 | import ( 4 | "crypto/rand" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/decentralized-identity/kerigo/pkg/derivation" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | // TODO: need to get test vectors to run against 13 | func TestBase(t *testing.T) { 14 | assert := assert.New(t) 15 | 16 | data := make([]byte, 32) 17 | int, err := rand.Read(data) 18 | assert.Equal(32, int) 19 | assert.Nil(err) 20 | 21 | der, err := derivation.New(derivation.WithCode(derivation.Ed25519NT), derivation.WithRaw(data)) 22 | if !assert.Nil(err) { 23 | return 24 | } 25 | 26 | bp := New(der) 27 | 28 | // stored the key correctly 29 | assert.Equal(data, bp.Raw()) 30 | 31 | // Generate the AID correctly 32 | aid := bp.String() 33 | assert.Len(aid, 44) 34 | assert.Equal(aid[:1], "B") 35 | 36 | // parse correctly 37 | bpn, err := FromString(aid) 38 | assert.Nil(err) 39 | assert.Equal(data, bpn.Raw()) 40 | 41 | // 42 | // self-addressing derivation 43 | // 44 | der, err = derivation.New(derivation.WithCode(derivation.Blake2b256), derivation.WithRaw(data)) 45 | assert.Nil(err) 46 | sap := New(der) 47 | 48 | // // Generate the AID correctly 49 | sapaid := sap.String() 50 | fmt.Println(sapaid) 51 | assert.Len(sapaid, 44) 52 | assert.Equal(sapaid[:1], "F") 53 | 54 | // parse correctly 55 | sapn, err := FromString(sapaid) 56 | assert.Nil(err) 57 | sapaidn := sapn.String() 58 | assert.Equal(sapaidn, sapaid) 59 | } 60 | -------------------------------------------------------------------------------- /pkg/prefix/main.go: -------------------------------------------------------------------------------- 1 | package prefix 2 | 3 | import ( 4 | "github.com/decentralized-identity/kerigo/pkg/derivation" 5 | ) 6 | 7 | type Type int 8 | 9 | const ( 10 | Basic Type = iota 11 | SelfAddressing 12 | ) 13 | 14 | // Traits are configuration options that indicate certain restrictions 15 | // on how the prefix is intended to be used. They are contained in the 16 | // "c" field of an event. 17 | type Trait int 18 | 19 | const ( 20 | EstablishmentOnly = iota 21 | DoNotDelegate 22 | ) 23 | 24 | var ( 25 | traitToString = map[Trait]string{ 26 | EstablishmentOnly: "EO", 27 | DoNotDelegate: "DND", 28 | } 29 | ) 30 | 31 | func (t Trait) String() string { 32 | return traitToString[t] 33 | } 34 | 35 | type Prefix interface { 36 | String() string 37 | Derivation() *derivation.Derivation 38 | Raw() []byte // Returns the RAW derived data 39 | } 40 | 41 | func New(derivation *derivation.Derivation) Prefix { 42 | // d, err := derivation.New(derivation.WithCode(dc), derivation.WithRaw(data)) 43 | // if err != nil { 44 | // return nil, err 45 | // } 46 | return &base{derivation: derivation} 47 | // data, err = bp.Derivation().Derive(data) 48 | // if err != nil { 49 | // return nil, err 50 | // } 51 | 52 | // bp.data = data 53 | 54 | // return bp, nil 55 | } 56 | 57 | func FromString(data string) (Prefix, error) { 58 | d, err := derivation.FromPrefix(data) 59 | if err != nil { 60 | return nil, err 61 | } 62 | 63 | return &base{derivation: d}, nil 64 | } 65 | -------------------------------------------------------------------------------- /pkg/derivation/ordinal.go: -------------------------------------------------------------------------------- 1 | package derivation 2 | 3 | import ( 4 | "encoding/base64" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "math/big" 9 | ) 10 | 11 | type Ordinal struct { 12 | num uint16 13 | } 14 | 15 | func NewOrdinal(val uint16) *Ordinal { 16 | return &Ordinal{num: val} 17 | } 18 | 19 | func (r *Ordinal) Base64() []byte { 20 | sint := new(big.Int) 21 | sint.SetUint64(uint64(r.num)) 22 | 23 | buf := make([]byte, 16) 24 | b := sint.Bytes() 25 | copy(buf[16-len(b):], b) 26 | 27 | dst := make([]byte, RandomSeed128.PrefixBase64Length()) 28 | base64.URLEncoding.Encode(dst, buf) 29 | 30 | code := RandomSeed128.String() 31 | out := append([]byte(code), dst[:len(dst)-len(code)]...) 32 | 33 | return out 34 | } 35 | 36 | func (r *Ordinal) Num() int { 37 | return int(r.num) 38 | } 39 | 40 | func ParseOrdinal(r io.Reader) (*Ordinal, error) { 41 | 42 | b64len := RandomSeed128.PrefixBase64Length() 43 | buf := make([]byte, b64len) 44 | 45 | c, err := r.Read(buf) 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | code := RandomSeed128.String() 51 | if c != b64len || string(buf[:len(code)]) != code { 52 | return nil, errors.New(fmt.Sprint("invalid ordinal", " ", string(buf), " ", c)) 53 | } 54 | 55 | dlen := RandomSeed128.PrefixDataLength() 56 | dst := make([]byte, dlen-2) 57 | 58 | b64 := append(buf[len(code):], []byte("==")...) 59 | 60 | _, err = base64.URLEncoding.Decode(dst, b64) 61 | if err != nil { 62 | return nil, err 63 | } 64 | 65 | sint := new(big.Int) 66 | sint.SetBytes(dst) 67 | 68 | return NewOrdinal(uint16(sint.Uint64())), nil 69 | } 70 | -------------------------------------------------------------------------------- /pkg/derivation/self-addressing.go: -------------------------------------------------------------------------------- 1 | package derivation 2 | 3 | import ( 4 | "crypto/sha256" 5 | "crypto/sha512" 6 | 7 | "golang.org/x/crypto/blake2b" 8 | "golang.org/x/crypto/blake2s" 9 | "golang.org/x/crypto/sha3" 10 | "lukechampine.com/blake3" 11 | ) 12 | 13 | func selfAddressingDeriver(c Code) (d deriver) { 14 | switch c { 15 | case Blake3256: 16 | d = func(data []byte) ([]byte, error) { 17 | hash := blake3.Sum256(data) 18 | return hash[:], nil 19 | } 20 | case Blake3512: 21 | d = func(data []byte) ([]byte, error) { 22 | hash := blake3.Sum512(data) 23 | return hash[:], nil 24 | } 25 | case Blake2b256: 26 | d = func(data []byte) ([]byte, error) { 27 | hash := blake2b.Sum256(data) 28 | return hash[:], nil 29 | } 30 | case Blake2b512: 31 | d = func(data []byte) ([]byte, error) { 32 | hash := blake2b.Sum512(data) 33 | return hash[:], nil 34 | } 35 | case Blake2s256: 36 | d = func(data []byte) ([]byte, error) { 37 | hash := blake2s.Sum256(data) 38 | return hash[:], nil 39 | } 40 | case SHA3256: 41 | d = func(data []byte) ([]byte, error) { 42 | hash := sha3.Sum256(data) 43 | return hash[:], nil 44 | } 45 | case SHA3512: 46 | d = func(data []byte) ([]byte, error) { 47 | hash := sha3.Sum512(data) 48 | return hash[:], nil 49 | } 50 | case SHA2256: 51 | d = func(data []byte) ([]byte, error) { 52 | hash := sha256.Sum256(data) 53 | return hash[:], nil 54 | } 55 | case SHA2512: 56 | d = func(data []byte) ([]byte, error) { 57 | hash := sha512.Sum512(data) 58 | return hash[:], nil 59 | } 60 | } 61 | 62 | return 63 | } 64 | -------------------------------------------------------------------------------- /pkg/db/badger/set.go: -------------------------------------------------------------------------------- 1 | package badger 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/dgraph-io/badger" 8 | "github.com/pkg/errors" 9 | ) 10 | 11 | type Set struct { 12 | keyspace string 13 | keyCode string 14 | iterator string 15 | } 16 | 17 | func NewSet(ns, key string) *Set { 18 | k := strings.Join([]string{"/", ns, key}, "") 19 | return &Set{ 20 | keyspace: ns, 21 | keyCode: k, 22 | iterator: iteratorFromKeyCode(k), 23 | } 24 | } 25 | 26 | func (r *Set) Get(txn *badger.Txn, keyvals ...interface{}) ([][]byte, error) { 27 | key := fmt.Sprintf(r.keyCode, keyvals...) 28 | return getVals(txn, key) 29 | } 30 | 31 | func (r *Set) Put(txn *badger.Txn, vals [][]byte, keyvals ...interface{}) error { 32 | key := fmt.Sprintf(r.keyCode, keyvals...) 33 | return addVals(txn, key, vals) 34 | } 35 | 36 | func (r *Set) Add(txn *badger.Txn, val []byte, keyvals ...interface{}) error { 37 | key := fmt.Sprintf(r.keyCode, keyvals...) 38 | return addVals(txn, key, [][]byte{val}) 39 | } 40 | 41 | func (r *Set) Count(txn *badger.Txn, keyvals ...interface{}) int { 42 | key := fmt.Sprintf(r.iterator, keyvals...) 43 | return countVals(txn, key) 44 | } 45 | 46 | func (r *Set) Delete(txn *badger.Txn, keyvals ...interface{}) error { 47 | key := fmt.Sprintf(r.keyCode, keyvals...) 48 | return delVals(txn, key) 49 | } 50 | 51 | func (r *Set) Iterator(txn *badger.Txn, keyvals ...interface{}) *SetIterator { 52 | seek := []byte(fmt.Sprintf(r.iterator, keyvals...)) 53 | return NewSetIterator(txn, seek) 54 | } 55 | 56 | func (r *Set) First(txn *badger.Txn, keyvals ...interface{}) ([][]byte, error) { 57 | seek := []byte(fmt.Sprintf(r.iterator, keyvals...)) 58 | it := NewSetIterator(txn, seek) 59 | defer it.Close() 60 | 61 | if !it.Next() { 62 | return nil, errors.New("not found") 63 | } 64 | 65 | return it.Value(), nil 66 | } 67 | 68 | func (r *Set) Last(txn *badger.Txn, keyvals ...interface{}) ([][]byte, error) { 69 | seek := []byte(fmt.Sprintf(r.iterator, keyvals...)) 70 | it := NewSetIterator(txn, seek) 71 | defer it.Close() 72 | 73 | for it.Next() { 74 | } 75 | 76 | return it.Value(), nil 77 | } 78 | -------------------------------------------------------------------------------- /pkg/main_test.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | import ( 4 | "crypto/ed25519" 5 | "crypto/rand" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | 10 | "github.com/decentralized-identity/kerigo/pkg/derivation" 11 | "github.com/decentralized-identity/kerigo/pkg/event" 12 | "github.com/decentralized-identity/kerigo/pkg/prefix" 13 | "github.com/decentralized-identity/kerigo/pkg/version" 14 | ) 15 | 16 | func TestNew(t *testing.T) { 17 | assert := assert.New(t) 18 | 19 | // ed25519 20 | edPub, _, err := ed25519.GenerateKey(rand.Reader) 21 | if !assert.Nil(err) { 22 | return 23 | } 24 | 25 | basicDerivation, err := derivation.New(derivation.WithCode(derivation.Ed25519), derivation.WithRaw(edPub)) 26 | assert.Nil(err) 27 | 28 | basicPre := prefix.New(basicDerivation) 29 | 30 | icp, err := event.NewInceptionEvent(event.WithKeys(basicPre)) 31 | assert.Nil(err) 32 | 33 | // create a self-addressing prefix for this 34 | 35 | // Serialize with defaults to get correct length for version string 36 | icp.Prefix = derivation.Blake2b256.Default() 37 | icp.Version = event.DefaultVersionString(event.JSON) 38 | eventBytes, err := event.Serialize(icp, event.JSON) 39 | assert.Nil(err) 40 | eventBytesExpected := len(eventBytes) 41 | 42 | //Generate correct version string 43 | icp.Version = event.VersionString(event.JSON, version.Code(), len(eventBytes)) 44 | 45 | // Serialize without the prefix for hasing 46 | icp.Prefix = "" 47 | eventBytes, err = event.Serialize(icp, event.JSON) 48 | assert.Nil(err) 49 | 50 | // Create the selfAddresing prefix 51 | saDerivation, err := derivation.New(derivation.WithCode(derivation.Blake2b256)) 52 | assert.Nil(err) 53 | 54 | _, err = saDerivation.Derive(eventBytes) 55 | assert.Nil(err) 56 | 57 | selfAdd := prefix.New(saDerivation) 58 | assert.Nil(err) 59 | selfAddAID := selfAdd.String() 60 | assert.Nil(err) 61 | 62 | // Set as the prefix for the inception event 63 | icp.Prefix = selfAddAID 64 | 65 | eventBytes, err = event.Serialize(icp, event.JSON) 66 | assert.Nil(err) 67 | 68 | // This should be the same length as was calculated for the version string 69 | assert.Equal(eventBytesExpected, len(eventBytes)) 70 | } 71 | -------------------------------------------------------------------------------- /pkg/derivation/counter_test.go: -------------------------------------------------------------------------------- 1 | package derivation 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestSigCounter_String(t *testing.T) { 8 | type fields struct { 9 | code CountCode 10 | count uint16 11 | length int 12 | } 13 | tests := []struct { 14 | name string 15 | fields fields 16 | want string 17 | wantErr bool 18 | }{ 19 | { 20 | name: "test 1", 21 | fields: fields{code: ControllerSigCountCode, count: 1, length: 2}, 22 | want: "-AAB", 23 | wantErr: false, 24 | }, 25 | { 26 | name: "test 2", 27 | fields: fields{code: ControllerSigCountCode, count: 2, length: 2}, 28 | want: "-AAC", 29 | wantErr: false, 30 | }, 31 | { 32 | name: "test 3", 33 | fields: fields{code: ControllerSigCountCode, count: 3, length: 2}, 34 | want: "-AAD", 35 | wantErr: false, 36 | }, 37 | { 38 | name: "test 256", 39 | fields: fields{code: ControllerSigCountCode, count: 256, length: 2}, 40 | want: "-AEA", 41 | wantErr: false, 42 | }, 43 | { 44 | name: "test 5000", 45 | fields: fields{code: ControllerSigCountCode, count: 5000, length: 2}, 46 | want: "", 47 | wantErr: true, 48 | }, 49 | { 50 | name: "transferable test 1", 51 | fields: fields{code: TransferableRctCountCode, count: 1, length: 2}, 52 | want: "-DAB", 53 | wantErr: false, 54 | }, 55 | { 56 | name: "transferable test 2", 57 | fields: fields{code: TransferableRctCountCode, count: 2, length: 2}, 58 | want: "-DAC", 59 | wantErr: false, 60 | }, 61 | { 62 | name: "transferable test 256", 63 | fields: fields{code: TransferableRctCountCode, count: 256, length: 2}, 64 | want: "-DEA", 65 | wantErr: false, 66 | }, 67 | } 68 | for _, tt := range tests { 69 | t.Run(tt.name, func(t *testing.T) { 70 | r := &Counter{ 71 | code: tt.fields.code, 72 | count: tt.fields.count, 73 | length: tt.fields.length, 74 | } 75 | got, err := r.String() 76 | if (err != nil) != tt.wantErr { 77 | t.Errorf("String() error = %v, wantErr %v", err, tt.wantErr) 78 | return 79 | } 80 | if got != tt.want { 81 | t.Errorf("String() got = %v, want %v", got, tt.want) 82 | } 83 | }) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The KERI Working Group is no longer active under DIF. Ongoing KERI work has moved to the [Web of Trust GitHub repository](https://github.com/WebOfTrust/keri). 2 | # 3 | 4 | # KERIGO (archived at DIF) 5 | Go implementation of KERI (Key Event Receipt Infrastructure) 6 | 7 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://raw.githubusercontent.com/decentralized-identity/kerigo/master/LICENSE) 8 | ![Go Version](https://img.shields.io/github/go-mod/go-version/decentralized-identity/kerigo) 9 | ![Build](https://github.com/decentralized-identity/kerigo/workflows/Build/badge.svg) 10 | [![codecov](https://codecov.io/gh/decentralized-identity/kerigo/branch/master/graph/badge.svg)](https://codecov.io/gh/decentralized-identity/kerigo) 11 | [![Go Report Card](https://goreportcard.com/badge/github.com/decentralized-identity/kerigo)](https://goreportcard.com/report/github.com/decentralized-identity/kerigo) 12 | # Introduction 13 | 14 | KERIGO is an open source go implementation of the [ Key Event Receipt Infrastructure (KERI) ](https://github.com/decentralized-identity/keri), a system designed to provide a secure identifier-based trust spanning layer for any stack. [The current version of the KERI paper can be found here](https://github.com/SmithSamuelM/Papers/blob/master/whitepapers/KERI_WP_2.x.web.pdf). 15 | 16 | KERI provides the same security and verifiability properties for transactions as a blockchain or distributed ledger can, without the overhead of requiring an absolute global ordering of transactions. Because of this, there is no need for a cannonical chain and thus there is no "KERI Chain" or "KERI Network". KERI Identifiers can be generated independantly in a self-sovereign and privacy-preserving manner and are secured via a self-certifying post-quantum resistant key management scheme based on blinded pre-rotation, auditable and flexible key events and a distributed conflict resolution algorithm called KAACE. 17 | 18 | # Features 19 | 20 | Currently work is focused on providing a libray that implements the core logic and data structures for creating and interacting with KERI infrastructure. A future goal is to implement runnable services that make up the actual KERI infrastructure (Witnesses, Validators, etc.). 21 | -------------------------------------------------------------------------------- /pkg/db/badger/set_test.go: -------------------------------------------------------------------------------- 1 | package badger 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | 7 | "github.com/dgraph-io/badger" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestSet_Add(t *testing.T) { 12 | td, cleanup := getTempDir(t) 13 | defer cleanup() 14 | 15 | db, err := badger.Open(badger.DefaultOptions(td)) 16 | assert.NoError(t, err) 17 | 18 | txn := db.NewTransaction(true) 19 | 20 | s := NewSet("fses", "/%s/%s") 21 | 22 | for i := 0; i < 10; i++ { 23 | d := strconv.Itoa(i) 24 | err := s.Add(txn, []byte(d), "abc", "xyz") 25 | assert.NoError(t, err) 26 | } 27 | 28 | for i := 0; i < 10; i++ { 29 | d := strconv.Itoa(i) 30 | err := s.Add(txn, []byte(d), "abc", "123") 31 | assert.NoError(t, err) 32 | } 33 | err = txn.Commit() 34 | assert.NoError(t, err) 35 | 36 | txn = db.NewTransaction(false) 37 | 38 | vals, err := s.Get(txn, "abc", "xyz") 39 | assert.NoError(t, err) 40 | assert.Len(t, vals, 10) 41 | 42 | for i, _ := range vals { 43 | d := strconv.Itoa(i) 44 | assert.Contains(t, vals, []byte(d)) 45 | } 46 | 47 | txn.Discard() 48 | } 49 | 50 | func TestSetModification(t *testing.T) { 51 | td, cleanup := getTempDir(t) 52 | defer cleanup() 53 | 54 | db, err := badger.Open(badger.DefaultOptions(td)) 55 | assert.NoError(t, err) 56 | 57 | txn := db.NewTransaction(true) 58 | 59 | s := NewSet("fses", "/%s/%s") 60 | 61 | for i := 0; i < 10; i++ { 62 | d := strconv.Itoa(i) 63 | err := s.Add(txn, []byte(d), "abc", "xyz") 64 | assert.NoError(t, err) 65 | } 66 | 67 | for i := 0; i < 10; i++ { 68 | d := strconv.Itoa(i + 10) 69 | err := s.Add(txn, []byte(d), "abc", "123") 70 | assert.NoError(t, err) 71 | } 72 | 73 | vals, err := s.First(txn, "abc") 74 | assert.NoError(t, err) 75 | assert.Len(t, vals, 10) 76 | assert.Contains(t, vals, []byte("10")) 77 | 78 | vals, err = s.Last(txn, "abc") 79 | assert.NoError(t, err) 80 | assert.Len(t, vals, 10) 81 | assert.Contains(t, vals, []byte("0")) 82 | 83 | err = s.Delete(txn, "abc", "xyz") 84 | assert.NoError(t, err) 85 | 86 | vals, err = s.First(txn, "abc") 87 | assert.NoError(t, err) 88 | assert.Len(t, vals, 10) 89 | assert.Contains(t, vals, []byte("10")) 90 | 91 | err = s.Put(txn, [][]byte{[]byte("16"), []byte("75")}, "abc", "123") 92 | assert.NoError(t, err) 93 | vals, err = s.First(txn, "abc") 94 | assert.NoError(t, err) 95 | assert.Len(t, vals, 11) 96 | } 97 | -------------------------------------------------------------------------------- /cmd/demo/eve/eve.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "net" 9 | "os" 10 | "time" 11 | 12 | "github.com/google/tink/go/aead" 13 | "github.com/google/tink/go/keyset" 14 | 15 | kbdgr "github.com/decentralized-identity/kerigo/pkg/db/badger" 16 | "github.com/decentralized-identity/kerigo/pkg/direct" 17 | "github.com/decentralized-identity/kerigo/pkg/keri" 18 | "github.com/decentralized-identity/kerigo/pkg/keymanager" 19 | ) 20 | 21 | var ( 22 | secrets = []string{ 23 | "AgjD4nRlycmM5cPcAkfOATAp8wVldRsnc9f1tiwctXlw", 24 | "AKUotEE0eAheKdDJh9QvNmSEmO_bjIav8V_GmctGpuCQ", 25 | "AK-nVhMMJciMPvmF5VZE_9H-nhrgng9aJWf7_UHPtRNM", 26 | "AT2cx-P5YUjIw_SLCHQ0pqoBWGk9s4N1brD-4pD_ANbs", 27 | "Ap5waegfnuP6ezC18w7jQiPyQwYYsp9Yv9rYMlKAYL8k", 28 | "Aqlc_FWWrxpxCo7R12uIz_Y2pHUH2prHx1kjghPa8jT8", 29 | "AagumsL8FeGES7tYcnr_5oN6qcwJzZfLKxoniKUpG4qc", 30 | "ADW3o9m3udwEf0aoOdZLLJdf1aylokP0lwwI_M2J9h0s", 31 | } 32 | km *keymanager.KeyManager 33 | ) 34 | 35 | func main() { 36 | e := flag.Int("e", 60, "Expire time for demo. Default is 60.0.") 37 | flag.Parse() 38 | 39 | td, err := ioutil.TempDir("", "keri-*") 40 | if err != nil { 41 | panic(err) 42 | } 43 | defer removeTempDir(td) 44 | 45 | db, err := kbdgr.New(td) 46 | if err != nil { 47 | panic(err) 48 | } 49 | defer db.Close() 50 | 51 | kh, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) 52 | if err != nil { 53 | panic(err) 54 | } 55 | 56 | a, err := aead.New(kh) 57 | if err != nil { 58 | panic(err) 59 | } 60 | 61 | km, err = keymanager.NewKeyManager(keymanager.WithAEAD(a), keymanager.WithStore(db), keymanager.WithSecrets(secrets)) 62 | if err != nil { 63 | panic(err) 64 | } 65 | 66 | kerl, err := keri.New(km, db) 67 | if err != nil { 68 | panic(err) 69 | } 70 | 71 | fmt.Printf("Direct Mode demo of Eve as %s on TCP port 5621 to port 5620\n\n\n", kerl.Prefix()) 72 | 73 | srv := &direct.Server{ 74 | Addr: ":5621", 75 | BaseIdentity: func(l net.Listener) *keri.Keri { 76 | return kerl 77 | }, 78 | } 79 | 80 | go func(t int) { 81 | select { 82 | case <-time.After(time.Duration(t) * time.Second): 83 | os.Exit(0) 84 | } 85 | }(*e) 86 | 87 | err = srv.ListenAndServe() 88 | log.Printf("direct mode server exited with %v\n", err) 89 | } 90 | 91 | func removeTempDir(td string) { 92 | err := os.RemoveAll(td) 93 | if err != nil { 94 | fmt.Println(err) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /pkg/db/badger/ordered_set.go: -------------------------------------------------------------------------------- 1 | package badger 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/dgraph-io/badger" 8 | "github.com/pkg/errors" 9 | ) 10 | 11 | type OrderedSet struct { 12 | keyspace string 13 | keyCode string 14 | iterator string 15 | } 16 | 17 | func NewOrderedSet(ns, key string) *OrderedSet { 18 | k := strings.Join([]string{"/", ns, key}, "") 19 | return &OrderedSet{ 20 | keyspace: ns, 21 | keyCode: k, 22 | iterator: iteratorFromKeyCode(k), 23 | } 24 | } 25 | 26 | func (r *OrderedSet) Get(txn *badger.Txn, keyvals ...interface{}) ([][]byte, error) { 27 | key := fmt.Sprintf(r.keyCode, keyvals...) 28 | return getOrderedVals(txn, key) 29 | } 30 | 31 | func (r *OrderedSet) Put(txn *badger.Txn, vals [][]byte, keyvals ...interface{}) error { 32 | key := fmt.Sprintf(r.keyCode, keyvals...) 33 | return addOrderedVals(txn, key, vals) 34 | } 35 | 36 | func (r *OrderedSet) Add(txn *badger.Txn, val []byte, keyvals ...interface{}) error { 37 | key := fmt.Sprintf(r.keyCode, keyvals...) 38 | return addOrderedVals(txn, key, [][]byte{val}) 39 | } 40 | 41 | func (r *OrderedSet) Count(txn *badger.Txn, keyvals ...interface{}) int { 42 | key := fmt.Sprintf(r.iterator, keyvals...) 43 | return countVals(txn, key) 44 | } 45 | 46 | func (r *OrderedSet) Delete(txn *badger.Txn, keyvals ...interface{}) error { 47 | key := fmt.Sprintf(r.keyCode, keyvals...) 48 | return delVals(txn, key) 49 | } 50 | 51 | func (r *OrderedSet) RemoveFromSet(txn *badger.Txn, val []byte, keyvals ...interface{}) error { 52 | key := fmt.Sprintf(r.keyCode, keyvals...) 53 | return removeFromVals(txn, key, val) 54 | } 55 | 56 | func (r *OrderedSet) Iterator(txn *badger.Txn, keyvals ...interface{}) *SetIterator { 57 | seek := []byte(fmt.Sprintf(r.iterator, keyvals...)) 58 | return NewSetIterator(txn, seek) 59 | } 60 | 61 | func (r *OrderedSet) First(txn *badger.Txn, keyvals ...interface{}) ([][]byte, error) { 62 | seek := []byte(fmt.Sprintf(r.iterator, keyvals...)) 63 | it := NewSetIterator(txn, seek) 64 | defer it.Close() 65 | 66 | if !it.Next() { 67 | return nil, errors.New("not found") 68 | } 69 | 70 | return it.Value(), nil 71 | } 72 | 73 | func (r *OrderedSet) Last(txn *badger.Txn, keyvals ...interface{}) ([][]byte, error) { 74 | seek := []byte(fmt.Sprintf(r.iterator, keyvals...)) 75 | it := NewSetIterator(txn, seek) 76 | defer it.Close() 77 | 78 | for it.Next() { 79 | } 80 | 81 | return it.Value(), nil 82 | } 83 | -------------------------------------------------------------------------------- /pkg/encoding/stream/stream_test.go: -------------------------------------------------------------------------------- 1 | package stream 2 | 3 | import ( 4 | "crypto/ed25519" 5 | "testing" 6 | 7 | "github.com/google/tink/go/signature/subtle" 8 | "github.com/stretchr/testify/assert" 9 | 10 | "github.com/decentralized-identity/kerigo/pkg/derivation" 11 | "github.com/decentralized-identity/kerigo/pkg/event" 12 | "github.com/decentralized-identity/kerigo/pkg/prefix" 13 | ) 14 | 15 | func TestMessageSerialization(t *testing.T) { 16 | expectedMsgBytes := `{"v":"KERI10JSON000000_","i":"Eh0fefvTQ55Jwps4dVnIekf7mZgWoU8bCUsDsKeGiEgU","s":"0","t":"icp","kt":"1","k":["D69EflciVP9zgsihNU14Dbm2bPXoNGxKHK_BBVFMQ-YU"],"n":"E2N7cav-AXF8R86YPUWqo8oGu2YcdyFz_w6lTiNmmOY4","wt":"0","w":[],"c":[]}-AABAA8UlKZCFEDmeWhk1MhyqwjXVobNEnjdApJ02k2ES3eDTT4jZBo8gZ0rdPRACS11xcCiXBYWLasL0bezI1JyzxBg` 17 | 18 | der, err := derivation.FromPrefix("ADW3o9m3udwEf0aoOdZLLJdf1aylokP0lwwI_M2J9h0s") 19 | assert.NoError(t, err) 20 | 21 | edPriv := ed25519.NewKeyFromSeed(der.Raw) 22 | edPub := edPriv.Public() 23 | signer, err := subtle.NewED25519SignerFromPrivateKey(&edPriv) 24 | 25 | keyDer, err := derivation.New(derivation.WithCode(derivation.Ed25519), derivation.WithRaw(edPub.(ed25519.PublicKey))) 26 | assert.NoError(t, err) 27 | keyPre := prefix.New(keyDer) 28 | 29 | der, err = derivation.FromPrefix("AagumsL8FeGES7tYcnr_5oN6qcwJzZfLKxoniKUpG4qc") 30 | assert.NoError(t, err) 31 | 32 | nextPriv := ed25519.NewKeyFromSeed(der.Raw) 33 | nextPub := nextPriv.Public() 34 | 35 | nextDer, err := derivation.New(derivation.WithCode(derivation.Ed25519), derivation.WithRaw(nextPub.(ed25519.PublicKey))) 36 | assert.NoError(t, err) 37 | nextKeyPre := prefix.New(nextDer) 38 | 39 | icp, err := event.NewInceptionEvent( 40 | event.WithPrefix("Eh0fefvTQ55Jwps4dVnIekf7mZgWoU8bCUsDsKeGiEgU"), 41 | event.WithKeys(keyPre), 42 | event.WithDefaultVersion(event.JSON), 43 | event.WithNext("1", derivation.Blake3256, nextKeyPre)) 44 | assert.NoError(t, err) 45 | 46 | d, err := icp.Serialize() 47 | assert.NoError(t, err) 48 | 49 | sig, err := derivation.New(derivation.WithCode(derivation.Ed25519Attached), derivation.WithSigner(signer.Sign)) 50 | assert.NoError(t, err) 51 | 52 | _, err = sig.Derive(d) 53 | assert.NoError(t, err) 54 | 55 | msg := &event.Message{ 56 | Event: icp, 57 | Signatures: []derivation.Derivation{*sig}, 58 | } 59 | 60 | b, err := ToDisjoint(msg) 61 | assert.NoError(t, err) 62 | 63 | assert.Equal(t, expectedMsgBytes, string(b)) 64 | 65 | } 66 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [ push, pull_request ] 3 | 4 | jobs: 5 | test: 6 | name: Unit Test 7 | runs-on: ubuntu-20.04 8 | strategy: 9 | matrix: 10 | go-version: [1.13, 1.14, 1.15] 11 | steps: 12 | - name: Set up Go ${{ matrix.go-version }} 13 | uses: actions/setup-go@v2 14 | with: 15 | go-version: ${{ matrix.go-version }} 16 | id: go 17 | 18 | - name: Check out code into the Go module directory 19 | uses: actions/checkout@v2 20 | 21 | - name: Test 22 | run: make test 23 | 24 | - name: Upload coverage to Codecov 25 | timeout-minutes: 5 26 | run: curl -s https://codecov.io/bash | bash 27 | 28 | interop-setup: 29 | runs-on: ubuntu-20.04 30 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 31 | outputs: 32 | CACHE_KEY_INTEROP: ${{ steps.cache.outputs.CACHE_KEY_INTEROP }} 33 | GITHUB_REPOSITORY_NAME: ${{ steps.cache.outputs.GITHUB_REPOSITORY_NAME }} 34 | steps: 35 | - name: Git checkout 36 | uses: actions/checkout@v2 37 | - name: Set outputs 38 | id: cache 39 | run: | 40 | echo "::set-output name=CACHE_KEY_INTEROP::${{ hashFiles('.github/workflows/interop/Dockerfile') }}" 41 | echo "::set-output name=GITHUB_REPOSITORY_NAME::$(echo ${GITHUB_REPOSITORY,,})" 42 | 43 | build-interop-image: 44 | needs: [interop-setup, test] 45 | runs-on: ubuntu-20.04 46 | env: 47 | DOCKER_BUILDKIT: 1 48 | CACHE_KEY_INTEROP: ${{ needs.interop-setup.outputs.CACHE_KEY_INTEROP }} 49 | GITHUB_REPOSITORY_NAME: ${{ needs.interop-setup.outputs.GITHUB_REPOSITORY_NAME }} 50 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 51 | steps: 52 | - name: Check out code into the Go module directory 53 | uses: actions/checkout@v2 54 | 55 | - name: 56 | run: | 57 | echo ${{ secrets.CR_PAT }} | docker login ghcr.io --username ${{ secrets.CR_USER }} --password-stdin 58 | docker build -f .github/workflows/interop/Dockerfile --no-cache -t ${{ env.GITHUB_REPOSITORY_NAME }}/kerigo-interop:${{ env.CACHE_KEY_INTEROP }} . 59 | docker tag ${{ env.GITHUB_REPOSITORY_NAME }}/kerigo-interop:${{ env.CACHE_KEY_INTEROP }} ghcr.io/${{ env.GITHUB_REPOSITORY_NAME }}/kerigo-interop:latest 60 | docker push ghcr.io/${{ env.GITHUB_REPOSITORY_NAME }}/kerigo-interop:latest 61 | mkdir -p ${GITHUB_WORKSPACE}/cache 62 | touch ${GITHUB_WORKSPACE}/cache/${{ env.CACHE_KEY_INTEROP }} 63 | -------------------------------------------------------------------------------- /pkg/direct/client.go: -------------------------------------------------------------------------------- 1 | package direct 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "net" 7 | "time" 8 | 9 | "github.com/cenkalti/backoff/v4" 10 | "github.com/pkg/errors" 11 | 12 | "github.com/decentralized-identity/kerigo/pkg/encoding/stream" 13 | "github.com/decentralized-identity/kerigo/pkg/event" 14 | "github.com/decentralized-identity/kerigo/pkg/keri" 15 | ) 16 | 17 | const ( 18 | DefaultReceiptTimeout = 60 * time.Second 19 | ) 20 | 21 | type Client struct { 22 | addr string 23 | ioc *conn 24 | id *keri.Keri 25 | } 26 | 27 | type Notify func(rcpt *event.Event, err error) 28 | 29 | func Dial(id *keri.Keri, addr string) (*Client, error) { 30 | return DialTimeout(id, addr, 0) 31 | } 32 | 33 | func DialTimeout(id *keri.Keri, addr string, timeout time.Duration) (*Client, error) { 34 | 35 | o := &Client{ 36 | addr: addr, 37 | id: id, 38 | } 39 | 40 | ctx, cancel := context.WithTimeout(context.Background(), timeout) 41 | defer cancel() 42 | 43 | err := backoff.Retry(func() error { 44 | var err error 45 | c, err := net.Dial("tcp", addr) 46 | if err != nil { 47 | return errors.Wrap(err, "unable to connect") 48 | } 49 | 50 | o.ioc = &conn{ 51 | reader: stream.NewReader(c), 52 | writer: stream.NewWriter(c), 53 | conn: c, 54 | } 55 | return nil 56 | }, backoff.WithContext(backoff.NewExponentialBackOff(), ctx)) 57 | 58 | if err != nil { 59 | return nil, errors.Wrap(err, "unable to connect after timeout") 60 | } 61 | 62 | log.Print(id.Prefix(), ":\n", "connected to", addr, "\n\n") 63 | 64 | go func() { 65 | err := handleConnection(o.ioc, o.id) 66 | log.Printf("client connection closed with (%v)\n", err) 67 | }() 68 | 69 | return o, nil 70 | 71 | } 72 | 73 | func (r *Client) Write(msg *event.Message) error { 74 | err := r.ioc.Write(msg) 75 | if err != nil { 76 | return errors.Wrap(err, "unable to right message to stream outbound") 77 | } 78 | 79 | return nil 80 | } 81 | 82 | func (r *Client) WriteNotify(msg *event.Message, n Notify) error { 83 | rcptCh, errCh := r.id.WaitForReceipt(msg.Event, DefaultReceiptTimeout) 84 | 85 | go func() { 86 | select { 87 | case rcpt := <-rcptCh: 88 | n(rcpt, nil) 89 | case err := <-errCh: 90 | n(nil, err) 91 | } 92 | }() 93 | 94 | err := r.ioc.Write(msg) 95 | if err != nil { 96 | return errors.Wrap(err, "unable to right message to stream outbound") 97 | } 98 | 99 | return nil 100 | } 101 | 102 | func (r *Client) Close() error { 103 | return r.ioc.conn.Close() 104 | } 105 | -------------------------------------------------------------------------------- /pkg/event/seal_test.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestDigestSeal(t *testing.T) { 12 | expectedBytes := `{"d":"EFGlDJesPRaa1AQJo8FD4DKR82VfVyp2Q027l4HhLTk8"}` 13 | s, err := NewDigestSeal("EFGlDJesPRaa1AQJo8FD4DKR82VfVyp2Q027l4HhLTk8") 14 | assert.NoError(t, err) 15 | 16 | sealBytes, err := json.Marshal(s) 17 | assert.NoError(t, err) 18 | assert.JSONEq(t, expectedBytes, string(sealBytes)) 19 | } 20 | 21 | func TestRootSeal(t *testing.T) { 22 | expectedBytes := `{"rd":"EFGlDJesPRaa1AQJo8FD4DKR82VfVyp2Q027l4HhLTk8"}` 23 | 24 | s, err := NewRootSeal("EFGlDJesPRaa1AQJo8FD4DKR82VfVyp2Q027l4HhLTk8") 25 | assert.NoError(t, err) 26 | 27 | sealBytes, err := json.Marshal(s) 28 | assert.NoError(t, err) 29 | assert.JSONEq(t, expectedBytes, string(sealBytes)) 30 | } 31 | 32 | func TestEventSeal(t *testing.T) { 33 | expectedBytes := `{"i":"ENqFtH6_cfDg8riLZ-GDvDaCKVn6clOJa7ZXXVXSWpRY","d":"EFGlDJesPRaa1AQJo8FD4DKR82VfVyp2Q027l4HhLTk8","s":"0"}` 34 | 35 | s, err := NewEventSeal("EFGlDJesPRaa1AQJo8FD4DKR82VfVyp2Q027l4HhLTk8", "ENqFtH6_cfDg8riLZ-GDvDaCKVn6clOJa7ZXXVXSWpRY", "0") 36 | assert.NoError(t, err) 37 | 38 | sealBytes, err := json.Marshal(s) 39 | assert.NoError(t, err) 40 | assert.JSONEq(t, expectedBytes, string(sealBytes)) 41 | } 42 | 43 | func TestEventLocationSeal(t *testing.T) { 44 | expectedBytes := `{"i":"ENqFtH6_cfDg8riLZ-GDvDaCKVn6clOJa7ZXXVXSWpRY","d":"EFGlDJesPRaa1AQJo8FD4DKR82VfVyp2Q027l4HhLTk8","s":"0","t":"vrc"}` 45 | 46 | s, err := NewEventLocationSeal("EFGlDJesPRaa1AQJo8FD4DKR82VfVyp2Q027l4HhLTk8", "ENqFtH6_cfDg8riLZ-GDvDaCKVn6clOJa7ZXXVXSWpRY", "0", VRC) 47 | assert.NoError(t, err) 48 | 49 | sealBytes, err := json.Marshal(s) 50 | assert.NoError(t, err) 51 | assert.JSONEq(t, expectedBytes, string(sealBytes)) 52 | } 53 | 54 | func TestSealEstablishment(t *testing.T) { 55 | expectedBytes := `{"i":"ENqFtH6_cfDg8riLZ-GDvDaCKVn6clOJa7ZXXVXSWpRY","s":"0","d":"EwUK1OhZGRyPaAt5Td-JzZkzSaDscKd_CtEKeUuwreHQ"}` 56 | 57 | evt := &Event{ 58 | Version: "KERI10JSON0000e6_", 59 | Prefix: "ENqFtH6_cfDg8riLZ-GDvDaCKVn6clOJa7ZXXVXSWpRY", 60 | Sequence: "0", 61 | EventType: "icp", 62 | } 63 | 64 | s, err := SealEstablishment(evt) 65 | assert.NoError(t, err) 66 | 67 | sealBytes, err := json.Marshal(s) 68 | assert.NoError(t, err) 69 | assert.Equal(t, expectedBytes, string(sealBytes)) 70 | 71 | } 72 | 73 | func TestSealSequenceInt(t *testing.T) { 74 | s := &Seal{Sequence: "4"} 75 | assert.Equal(t, 4, s.SequenceInt()) 76 | 77 | s.Sequence = fmt.Sprintf("%x", 93840482) 78 | assert.Equal(t, 93840482, s.SequenceInt()) 79 | } 80 | -------------------------------------------------------------------------------- /pkg/event/message.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/decentralized-identity/kerigo/pkg/derivation" 7 | ) 8 | 9 | // an event message holds the deserialized event 10 | // along with the provided signature 11 | type Message struct { 12 | Event *Event 13 | Signatures []derivation.Derivation 14 | TransferableReceipts []*Receipt 15 | NonTransferableReceipts []*Receipt 16 | WitnessReceipts []*Receipt 17 | } 18 | 19 | type MessageOption func(*Message) error 20 | 21 | func NewMessage(evt *Event, opts ...MessageOption) (*Message, error) { 22 | msg := &Message{Event: evt} 23 | 24 | for _, opt := range opts { 25 | err := opt(msg) 26 | if err != nil { 27 | return nil, err 28 | } 29 | } 30 | 31 | return msg, nil 32 | } 33 | 34 | func WithSignatures(sigs []derivation.Derivation) MessageOption { 35 | return func(msg *Message) error { 36 | msg.Signatures = append(msg.Signatures, sigs...) 37 | return nil 38 | } 39 | } 40 | 41 | func WithTransferableReceipts(quads []*Quadlet) MessageOption { 42 | return func(msg *Message) error { 43 | rcts := make([]*Receipt, len(quads)) 44 | for i, quad := range quads { 45 | rct, err := NewReceipt(msg.Event, 46 | WithSignature(quad.Signature), 47 | WithEstablishmentSeal(&Seal{ 48 | Prefix: quad.Prefix.AsPrefix(), 49 | Sequence: strconv.Itoa(quad.Sequence), 50 | Digest: quad.Digest.AsPrefix(), 51 | }), 52 | ) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | rcts[i] = rct 58 | } 59 | msg.TransferableReceipts = append(msg.TransferableReceipts, rcts...) 60 | return nil 61 | } 62 | } 63 | 64 | func WithNonTransferableReceipts(couples []*Couplet) MessageOption { 65 | return func(msg *Message) error { 66 | rcts := make([]*Receipt, len(couples)) 67 | for i, couple := range couples { 68 | rct, err := NewReceipt(msg.Event, WithSignerPrefix(couple.Prefix.AsPrefix()), 69 | WithSignature(couple.Signature)) 70 | if err != nil { 71 | return err 72 | } 73 | 74 | rcts[i] = rct 75 | } 76 | msg.NonTransferableReceipts = append(msg.NonTransferableReceipts, rcts...) 77 | return nil 78 | } 79 | } 80 | 81 | func WithWitnessReceipts(couples []*Couplet) MessageOption { 82 | return func(msg *Message) error { 83 | rcts := make([]*Receipt, len(couples)) 84 | for i, couple := range couples { 85 | rct, err := NewReceipt(msg.Event, WithSignerPrefix(couple.Prefix.AsPrefix()), 86 | WithSignature(couple.Signature)) 87 | if err != nil { 88 | return err 89 | } 90 | 91 | rcts[i] = rct 92 | } 93 | msg.WitnessReceipts = append(msg.WitnessReceipts, rcts...) 94 | return nil 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /pkg/db/badger/ordered_set_test.go: -------------------------------------------------------------------------------- 1 | package badger 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | 7 | "github.com/dgraph-io/badger" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestOrderedSet_Add(t *testing.T) { 12 | td, cleanup := getTempDir(t) 13 | defer cleanup() 14 | 15 | db, err := badger.Open(badger.DefaultOptions(td)) 16 | assert.NoError(t, err) 17 | 18 | txn := db.NewTransaction(true) 19 | 20 | s := NewOrderedSet("fses", "/%s/%s") 21 | 22 | for i := 0; i < 10; i++ { 23 | d := strconv.Itoa(i) 24 | err := s.Add(txn, []byte(d), "abc", "xyz") 25 | assert.NoError(t, err) 26 | } 27 | 28 | for i := 0; i < 10; i++ { 29 | d := strconv.Itoa(i) 30 | err := s.Add(txn, []byte(d), "abc", "123") 31 | assert.NoError(t, err) 32 | } 33 | err = txn.Commit() 34 | assert.NoError(t, err) 35 | 36 | txn = db.NewTransaction(false) 37 | 38 | vals, err := s.Get(txn, "abc", "xyz") 39 | assert.NoError(t, err) 40 | assert.Len(t, vals, 10) 41 | 42 | for i, val := range vals { 43 | d := strconv.Itoa(i) 44 | assert.Equal(t, d, string(val)) 45 | } 46 | 47 | txn.Discard() 48 | } 49 | 50 | func TestModification(t *testing.T) { 51 | td, cleanup := getTempDir(t) 52 | defer cleanup() 53 | 54 | db, err := badger.Open(badger.DefaultOptions(td)) 55 | assert.NoError(t, err) 56 | 57 | txn := db.NewTransaction(true) 58 | 59 | s := NewOrderedSet("fses", "/%s/%s") 60 | 61 | for i := 0; i < 10; i++ { 62 | d := strconv.Itoa(i) 63 | err := s.Add(txn, []byte(d), "abc", "xyz") 64 | assert.NoError(t, err) 65 | } 66 | 67 | for i := 0; i < 10; i++ { 68 | d := strconv.Itoa(i + 10) 69 | err := s.Add(txn, []byte(d), "abc", "123") 70 | assert.NoError(t, err) 71 | } 72 | 73 | vals, err := s.First(txn, "abc") 74 | assert.NoError(t, err) 75 | assert.Len(t, vals, 10) 76 | assert.Equal(t, "10", string(vals[0])) 77 | 78 | vals, err = s.Last(txn, "abc") 79 | assert.NoError(t, err) 80 | assert.Len(t, vals, 10) 81 | assert.Equal(t, "0", string(vals[0])) 82 | 83 | err = s.Delete(txn, "abc", "xyz") 84 | assert.NoError(t, err) 85 | 86 | vals, err = s.First(txn, "abc") 87 | assert.NoError(t, err) 88 | assert.Len(t, vals, 10) 89 | assert.Equal(t, "10", string(vals[0])) 90 | 91 | err = s.RemoveFromSet(txn, []byte("15"), "abc", "123") 92 | assert.NoError(t, err) 93 | vals, err = s.First(txn, "abc") 94 | assert.NoError(t, err) 95 | assert.Len(t, vals, 9) 96 | 97 | err = s.Put(txn, [][]byte{[]byte("16"), []byte("75")}, "abc", "123") 98 | assert.NoError(t, err) 99 | vals, err = s.First(txn, "abc") 100 | assert.NoError(t, err) 101 | assert.Len(t, vals, 10) 102 | } 103 | -------------------------------------------------------------------------------- /pkg/derivation/counter.go: -------------------------------------------------------------------------------- 1 | package derivation 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type CountCode int 8 | 9 | const ( 10 | ControllerSigCountCode CountCode = iota 11 | WitnessSigCountCode 12 | NonTransferableRctCountCode 13 | TransferableRctCountCode 14 | FirstSeenReplayCountCode 15 | MessageDataGroupCountCode 16 | AttachedMaterialCountCode 17 | MessageDataMaterialCountCode 18 | CombinedMaterialCountCode 19 | MaterialGroupCountCode 20 | MaterialCountCode 21 | 22 | SigCountLen = 2 23 | ) 24 | 25 | var ( 26 | countCodeString = map[CountCode]string{ 27 | ControllerSigCountCode: "-A", 28 | WitnessSigCountCode: "-B", 29 | NonTransferableRctCountCode: "-C", 30 | TransferableRctCountCode: "-D", 31 | FirstSeenReplayCountCode: "-E", 32 | MessageDataGroupCountCode: "-U", 33 | AttachedMaterialCountCode: "-V", 34 | MessageDataMaterialCountCode: "-W", 35 | CombinedMaterialCountCode: "-X", 36 | MaterialGroupCountCode: "-Y", 37 | MaterialCountCode: "-Z", 38 | } 39 | 40 | CountCodes = map[string]CountCode{ 41 | "-A": ControllerSigCountCode, 42 | "-B": WitnessSigCountCode, 43 | "-C": NonTransferableRctCountCode, 44 | "-D": TransferableRctCountCode, 45 | "-E": FirstSeenReplayCountCode, 46 | "-U": MessageDataGroupCountCode, 47 | "-V": AttachedMaterialCountCode, 48 | "-W": MessageDataMaterialCountCode, 49 | "-X": CombinedMaterialCountCode, 50 | "-Y": MaterialGroupCountCode, 51 | "-Z": MaterialCountCode, 52 | } 53 | ) 54 | 55 | type CountOpt func(*Counter) error 56 | 57 | type Counter struct { 58 | code CountCode 59 | count uint16 60 | length int 61 | } 62 | 63 | func NewSigCounter(code CountCode, opts ...CountOpt) (*Counter, error) { 64 | s := &Counter{ 65 | code: code, 66 | count: 1, 67 | } 68 | 69 | for _, o := range opts { 70 | err := o(s) 71 | if err != nil { 72 | return nil, err 73 | } 74 | } 75 | 76 | return s, nil 77 | } 78 | 79 | func (r *Counter) Incr() uint16 { 80 | r.count++ 81 | return r.count 82 | } 83 | 84 | func (r *Counter) IncrBy(i uint16) uint16 { 85 | r.count += i 86 | return r.count 87 | } 88 | 89 | func (r *Counter) String() (string, error) { 90 | b64, err := IndexToBase64(r.count) 91 | if err != nil { 92 | return "", fmt.Errorf("unable to base64 encode signature count: %v", err) 93 | } 94 | 95 | cs := countCodeString[r.code] 96 | 97 | pad := SigCountLen - len(b64) 98 | return fmt.Sprintf("%s%.*s%s", cs, pad, "A", b64), nil 99 | } 100 | 101 | func WithCount(count int) CountOpt { 102 | return func(s *Counter) error { 103 | s.count = uint16(count) 104 | return nil 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /pkg/db/badger/iterator.go: -------------------------------------------------------------------------------- 1 | package badger 2 | 3 | import ( 4 | "bytes" 5 | 6 | "github.com/dgraph-io/badger" 7 | ) 8 | 9 | type Iterator struct { 10 | it *badger.Iterator 11 | seek []byte 12 | start bool 13 | } 14 | 15 | func NewIterator(txn *badger.Txn, seek []byte) *Iterator { 16 | opts := badger.DefaultIteratorOptions 17 | opts.Prefix = seek 18 | 19 | it := txn.NewIterator(opts) 20 | 21 | return &Iterator{it, seek, false} 22 | } 23 | 24 | func (r *Iterator) Next() bool { 25 | if !r.start { 26 | r.it.Rewind() 27 | r.start = true 28 | return r.it.Valid() 29 | } 30 | 31 | r.it.Next() 32 | 33 | return r.it.Valid() 34 | } 35 | 36 | func (r *Iterator) Value() []byte { 37 | if !r.it.Valid() { 38 | return nil 39 | } 40 | 41 | item := r.it.Item() 42 | out, err := item.ValueCopy(nil) 43 | if err != nil { 44 | return nil 45 | } 46 | 47 | return out 48 | } 49 | 50 | func (r *Iterator) Close() { 51 | r.it.Close() 52 | } 53 | 54 | func (r *Iterator) Key() []byte { 55 | if !r.it.Valid() { 56 | return nil 57 | } 58 | 59 | return r.it.Item().KeyCopy(nil) 60 | } 61 | 62 | type SetIterator struct { 63 | it *badger.Iterator 64 | seek []byte 65 | curIdx []byte 66 | curKey []byte 67 | curVal [][]byte 68 | } 69 | 70 | func NewSetIterator(txn *badger.Txn, seek []byte) *SetIterator { 71 | opts := badger.DefaultIteratorOptions 72 | opts.Prefix = seek 73 | 74 | it := txn.NewIterator(opts) 75 | 76 | return &SetIterator{ 77 | it: it, 78 | seek: seek, 79 | } 80 | } 81 | 82 | func (r *SetIterator) Next() bool { 83 | if len(r.curIdx) == 0 { 84 | r.it.Seek(r.seek) 85 | if !r.it.Valid() { 86 | return false 87 | } 88 | 89 | item := r.it.Item() 90 | k := item.KeyCopy(nil) 91 | 92 | r.curKey = k[:len(k)-32] 93 | r.curIdx = k[:len(k)-32] 94 | } 95 | 96 | return r.batch() 97 | } 98 | 99 | func (r *SetIterator) batch() bool { 100 | if !r.it.Valid() { 101 | return false 102 | } 103 | 104 | var root []byte 105 | for r.curVal = [][]byte{}; r.it.Valid(); r.it.Next() { 106 | item := r.it.Item() 107 | k := item.KeyCopy(nil) 108 | 109 | root = k[:len(k)-32] 110 | 111 | if bytes.Compare(root, r.curIdx) != 0 { 112 | break 113 | } 114 | 115 | val, err := item.ValueCopy(nil) 116 | if err != nil { 117 | return false 118 | } 119 | 120 | r.curVal = append(r.curVal, val) 121 | 122 | } 123 | 124 | r.curKey = r.curIdx 125 | r.curIdx = root 126 | 127 | return true 128 | } 129 | 130 | func (r *SetIterator) Value() [][]byte { 131 | if r.curVal == nil { 132 | return nil 133 | } 134 | return r.curVal 135 | } 136 | 137 | func (r *SetIterator) Key() []byte { 138 | return r.curKey 139 | } 140 | 141 | func (r *SetIterator) Close() { 142 | r.it.Close() 143 | } 144 | -------------------------------------------------------------------------------- /pkg/log/escrow.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "github.com/decentralized-identity/kerigo/pkg/derivation" 5 | "github.com/decentralized-identity/kerigo/pkg/event" 6 | ) 7 | 8 | type Escrow map[string]*event.Message 9 | 10 | // Get returns the event message with all collected signatures 11 | // for the given event 12 | func (e Escrow) Get(evnt *event.Event) (*event.Message, error) { 13 | digest, err := digestEvent(evnt) 14 | if err != nil { 15 | return nil, err 16 | } 17 | 18 | escrowed := &event.Message{Event: evnt} 19 | 20 | if esc, ok := e[digest]; ok { 21 | escrowed = esc 22 | } 23 | 24 | return escrowed, nil 25 | } 26 | 27 | // ForSequence returns all of the messages currently in escrow for the given sequence number 28 | func (e Escrow) ForSequence(sequence int) []*event.Message { 29 | msgs := []*event.Message{} 30 | for k, msg := range e { 31 | if msg.Event.SequenceInt() == sequence { 32 | msgs = append(msgs, e[k]) 33 | } 34 | } 35 | 36 | return msgs 37 | } 38 | 39 | // Add a message to the escrow 40 | func (e Escrow) Add(m *event.Message) error { 41 | digest, err := digestEvent(m.Event) 42 | if err != nil { 43 | return err 44 | } 45 | 46 | if _, ok := e[digest]; !ok { 47 | e[digest] = m 48 | } else { 49 | e[digest].Signatures = mergeSignatures(e[digest].Signatures, m.Signatures) 50 | } 51 | 52 | return nil 53 | } 54 | 55 | // Remove an event from the escrow 56 | func (e Escrow) Remove(m *event.Message) error { 57 | digest, err := digestEvent(m.Event) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | delete(e, digest) 63 | return nil 64 | } 65 | 66 | // Clear all escrowed messages with the same sequence number 67 | // Escrowed events are indexed by the digest of their event - there could be 68 | // competeing versions of events if the prefix owner is being duplicitous, but 69 | // the first valid seen event always wins 70 | // Thus, this goes through and drops all competing events from the escrow 71 | // and returns them 72 | func (e Escrow) Clear(evnt event.Event) ([]*event.Message, error) { 73 | sequence := evnt.SequenceInt() 74 | digest, err := digestEvent(&evnt) 75 | if err != nil { 76 | return nil, err 77 | } 78 | 79 | delete(e, digest) 80 | 81 | dups := []*event.Message{} 82 | for i, msg := range e { 83 | if msg.Event.SequenceInt() == sequence { 84 | dups = append(dups, e[i]) 85 | delete(e, i) 86 | } 87 | } 88 | 89 | return dups, nil 90 | } 91 | 92 | // digestEvent creates a standard digest of events to use as their index in 93 | // the escrow 94 | func digestEvent(evnt *event.Event) (string, error) { 95 | serialized, err := evnt.Serialize() 96 | if err != nil { 97 | return "", err 98 | } 99 | 100 | return event.DigestString(serialized, derivation.Blake3256) 101 | } 102 | -------------------------------------------------------------------------------- /cmd/demo/sam/sam.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "time" 9 | 10 | "github.com/google/tink/go/aead" 11 | "github.com/google/tink/go/keyset" 12 | 13 | kbdgr "github.com/decentralized-identity/kerigo/pkg/db/badger" 14 | "github.com/decentralized-identity/kerigo/pkg/direct" 15 | "github.com/decentralized-identity/kerigo/pkg/event" 16 | "github.com/decentralized-identity/kerigo/pkg/keri" 17 | "github.com/decentralized-identity/kerigo/pkg/keymanager" 18 | ) 19 | 20 | var ( 21 | secrets = []string{ 22 | "ArwXoACJgOleVZ2PY7kXn7rA0II0mHYDhc6WrBH8fDAc", 23 | "A6zz7M08-HQSFq92sJ8KJOT2cZ47x7pXFQLPB0pckB3Q", 24 | "AcwFTk-wgk3ZT2buPRIbK-zxgPx-TKbaegQvPEivN90Y", 25 | "Alntkt3u6dDgiQxTATr01dy8M72uuaZEf9eTdM-70Gk8", 26 | "A1-QxDkso9-MR1A8rZz_Naw6fgaAtayda8hrbkRVVu1E", 27 | "AKuYMe09COczwf2nIoD5AE119n7GLFOVFlNLxZcKuswc", 28 | "AxFfJTcSuEE11FINfXMqWttkZGnUZ8KaREhrnyAXTsjw", 29 | "ALq-w1UKkdrppwZzGTtz4PWYEeWm0-sDHzOv5sq96xJY", 30 | } 31 | km *keymanager.KeyManager 32 | ) 33 | 34 | func main() { 35 | e := flag.Int("e", 60, "Expire time for demo. Default is 60.0.") 36 | flag.Parse() 37 | 38 | td, err := ioutil.TempDir("", "keri-*") 39 | if err != nil { 40 | panic(err) 41 | } 42 | 43 | db, err := kbdgr.New(td) 44 | if err != nil { 45 | panic(err) 46 | } 47 | 48 | kh, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) 49 | if err != nil { 50 | panic(err) 51 | } 52 | 53 | a, err := aead.New(kh) 54 | if err != nil { 55 | panic(err) 56 | } 57 | 58 | km, err = keymanager.NewKeyManager(keymanager.WithAEAD(a), keymanager.WithStore(db), keymanager.WithSecrets(secrets)) 59 | if err != nil { 60 | panic(err) 61 | } 62 | 63 | kerl, err := keri.New(km, db) 64 | if err != nil { 65 | panic(err) 66 | } 67 | 68 | fmt.Printf("Direct Mode demo of Sam as %s on TCP port 5620 to port 5621\n\n\n", kerl.Prefix()) 69 | 70 | cli, err := direct.DialTimeout(kerl, ":5621", time.Duration(*e)*time.Second) 71 | if err != nil { 72 | panic(err) 73 | } 74 | 75 | msg, err := kerl.Inception() 76 | if err != nil { 77 | panic(err) 78 | } 79 | 80 | err = cli.WriteNotify(msg, func(rcpt *event.Event, err error) { 81 | if err != nil { 82 | panic(err) 83 | } 84 | 85 | ixn, err := kerl.Interaction([]*event.Seal{}) 86 | if err != nil { 87 | panic(err) 88 | } 89 | 90 | err = cli.WriteNotify(ixn, func(rcpt *event.Event, err error) { 91 | if err != nil { 92 | panic(err) 93 | } 94 | 95 | rot, err := kerl.Rotate() 96 | if err != nil { 97 | panic(err) 98 | } 99 | 100 | err = cli.Write(rot) 101 | if err != nil { 102 | panic(err) 103 | } 104 | 105 | }) 106 | if err != nil { 107 | panic(err) 108 | } 109 | }) 110 | 111 | select { 112 | case <-time.After(time.Duration(*e) * time.Second): 113 | os.Exit(0) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /pkg/db/badger/value.go: -------------------------------------------------------------------------------- 1 | package badger 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/dgraph-io/badger" 8 | ) 9 | 10 | type Value struct { 11 | keyspace string 12 | keyCode string 13 | iterator string 14 | } 15 | 16 | func NewValue(ns, key string) *Value { 17 | k := strings.Join([]string{"/", ns, key}, "") 18 | return &Value{ 19 | keyspace: ns, 20 | keyCode: k, 21 | iterator: iteratorFromKeyCode(k), 22 | } 23 | } 24 | 25 | func (r *Value) Exists(txn *badger.Txn, keyvals ...interface{}) bool { 26 | key := fmt.Sprintf(r.keyCode, keyvals...) 27 | _, err := txn.Get([]byte(key)) 28 | 29 | return err == nil 30 | } 31 | 32 | func (r *Value) Get(txn *badger.Txn, keyvals ...interface{}) ([]byte, error) { 33 | key := fmt.Sprintf(r.keyCode, keyvals...) 34 | item, err := txn.Get([]byte(key)) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | out, err := item.ValueCopy(nil) 40 | return out, err 41 | } 42 | 43 | // Set writes value at key, overwrites if it already exists 44 | func (r *Value) Set(txn *badger.Txn, val []byte, keyvals ...interface{}) error { 45 | key := fmt.Sprintf(r.keyCode, keyvals...) 46 | 47 | err := txn.Set([]byte(key), val) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | return nil 53 | } 54 | 55 | // Put writes value at key, does not change value if it already exsits 56 | func (r *Value) Put(txn *badger.Txn, val []byte, keyvals ...interface{}) error { 57 | key := fmt.Sprintf(r.keyCode, keyvals...) 58 | 59 | _, err := txn.Get([]byte(key)) 60 | if err == nil { 61 | return nil 62 | } 63 | 64 | err = txn.Set([]byte(key), val) 65 | if err != nil { 66 | return err 67 | } 68 | 69 | return nil 70 | } 71 | 72 | func (r *Value) Iterator(txn *badger.Txn, keyvals ...interface{}) *Iterator { 73 | seek := []byte(fmt.Sprintf(r.iterator, keyvals...)) 74 | return NewIterator(txn, seek) 75 | } 76 | 77 | func (r *Value) Count(txn *badger.Txn, keyvals ...interface{}) int { 78 | key := fmt.Sprintf(r.iterator, keyvals...) 79 | return countVals(txn, key) 80 | } 81 | 82 | func (r *Value) First(txn *badger.Txn, keyvals ...interface{}) ([]byte, error) { 83 | seek := fmt.Sprintf(r.iterator, keyvals...) 84 | val, err := distalVal(txn, seek, false) 85 | if err != nil { 86 | return nil, err 87 | } 88 | 89 | return val, nil 90 | } 91 | 92 | func (r *Value) Last(txn *badger.Txn, keyvals ...interface{}) ([]byte, error) { 93 | seek := fmt.Sprintf(r.iterator, keyvals...) 94 | val, err := distalVal(txn, seek, true) 95 | if err != nil { 96 | return nil, err 97 | } 98 | 99 | return val, nil 100 | } 101 | 102 | func (r *Value) Delete(txn *badger.Txn, keyvals ...interface{}) error { 103 | key := fmt.Sprintf(r.keyCode, keyvals...) 104 | return txn.Delete([]byte(key)) 105 | } 106 | 107 | func (r *Value) DeleteAll(txn *badger.Txn, keyvals ...interface{}) error { 108 | key := fmt.Sprintf(r.iterator, keyvals...) 109 | return delVals(txn, key) 110 | } 111 | -------------------------------------------------------------------------------- /pkg/event/attachment.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | 7 | "github.com/pkg/errors" 8 | 9 | "github.com/decentralized-identity/kerigo/pkg/derivation" 10 | ) 11 | 12 | type AttachmentParser func(buf io.Reader) ([]derivation.Derivation, error) 13 | 14 | type Attachment struct { 15 | Code derivation.CountCode 16 | Signatures []derivation.Derivation 17 | TransferableReceipts []*Quadlet 18 | NonTransferableReceipts []*Couplet 19 | WitnessReceipts []*Couplet 20 | } 21 | 22 | type Couplet struct { 23 | Prefix *derivation.Derivation 24 | Signature *derivation.Derivation 25 | } 26 | 27 | type Quadlet struct { 28 | Prefix *derivation.Derivation 29 | Signature *derivation.Derivation 30 | Digest *derivation.Derivation 31 | Sequence int 32 | } 33 | 34 | func ParseAttachment(rd io.Reader) (*Attachment, error) { 35 | buf := bufio.NewReader(rd) 36 | 37 | f, err := buf.Peek(5) 38 | if err != nil { 39 | return nil, errors.Wrap(err, "error peeking") 40 | } 41 | 42 | if f[0] != '-' && f[0] != '_' { 43 | return nil, errors.New("invalid text attachment code") 44 | } 45 | 46 | c := string(f[:2]) 47 | countCode, ok := derivation.CountCodes[c] 48 | if ok { 49 | att, err := ParseAttached(countCode, buf) 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | return att, nil 55 | } 56 | 57 | return nil, errors.New("invalid attachment code") 58 | } 59 | 60 | func ParseAttached(c derivation.CountCode, buf io.Reader) (*Attachment, error) { 61 | switch c { 62 | case derivation.ControllerSigCountCode: 63 | ders, err := derivation.ParseAttachedSignatures(buf) 64 | if err != nil { 65 | return nil, errors.Wrap(err, "error reading attached signatures") 66 | } 67 | 68 | return &Attachment{ 69 | Code: derivation.ControllerSigCountCode, 70 | Signatures: ders, 71 | }, nil 72 | case derivation.WitnessSigCountCode: 73 | rcpts, err := ParseAttachedCouplets(buf) 74 | if err != nil { 75 | return nil, errors.Wrap(err, "error reading attached signatures") 76 | } 77 | 78 | return &Attachment{ 79 | Code: derivation.WitnessSigCountCode, 80 | WitnessReceipts: rcpts, 81 | }, nil 82 | case derivation.NonTransferableRctCountCode: 83 | rcpts, err := ParseAttachedCouplets(buf) 84 | if err != nil { 85 | return nil, errors.Wrap(err, "error reading attached signatures") 86 | } 87 | 88 | return &Attachment{ 89 | Code: derivation.NonTransferableRctCountCode, 90 | NonTransferableReceipts: rcpts, 91 | }, nil 92 | case derivation.TransferableRctCountCode: 93 | rcpts, err := ParseAttachedQuadlets(buf) 94 | if err != nil { 95 | return nil, errors.Wrap(err, "error reading attached signatures") 96 | } 97 | 98 | return &Attachment{ 99 | Code: derivation.TransferableRctCountCode, 100 | TransferableReceipts: rcpts, 101 | }, nil 102 | } 103 | 104 | return nil, errors.New("not implemented") 105 | } 106 | -------------------------------------------------------------------------------- /cmd/demo/bob/bob.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "time" 9 | 10 | "github.com/google/tink/go/aead" 11 | "github.com/google/tink/go/keyset" 12 | 13 | kbdgr "github.com/decentralized-identity/kerigo/pkg/db/badger" 14 | "github.com/decentralized-identity/kerigo/pkg/direct" 15 | "github.com/decentralized-identity/kerigo/pkg/event" 16 | "github.com/decentralized-identity/kerigo/pkg/keri" 17 | "github.com/decentralized-identity/kerigo/pkg/keymanager" 18 | ) 19 | 20 | var ( 21 | secrets = []string{ 22 | "ArwXoACJgOleVZ2PY7kXn7rA0II0mHYDhc6WrBH8fDAc", 23 | "A6zz7M08-HQSFq92sJ8KJOT2cZ47x7pXFQLPB0pckB3Q", 24 | "AcwFTk-wgk3ZT2buPRIbK-zxgPx-TKbaegQvPEivN90Y", 25 | "Alntkt3u6dDgiQxTATr01dy8M72uuaZEf9eTdM-70Gk8", 26 | "A1-QxDkso9-MR1A8rZz_Naw6fgaAtayda8hrbkRVVu1E", 27 | "AKuYMe09COczwf2nIoD5AE119n7GLFOVFlNLxZcKuswc", 28 | "AxFfJTcSuEE11FINfXMqWttkZGnUZ8KaREhrnyAXTsjw", 29 | "ALq-w1UKkdrppwZzGTtz4PWYEeWm0-sDHzOv5sq96xJY", 30 | } 31 | km *keymanager.KeyManager 32 | ) 33 | 34 | func main() { 35 | e := flag.Int("e", 60, "Expire time for demo. Default is 60.0.") 36 | flag.Parse() 37 | 38 | td, err := ioutil.TempDir("", "keri-bob-*") 39 | if err != nil { 40 | panic(err) 41 | } 42 | defer removeTempDir(td) 43 | 44 | db, err := kbdgr.New(td) 45 | if err != nil { 46 | panic(err) 47 | } 48 | defer db.Close() 49 | 50 | kh, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) 51 | if err != nil { 52 | panic(err) 53 | } 54 | 55 | a, err := aead.New(kh) 56 | if err != nil { 57 | panic(err) 58 | } 59 | 60 | km, err = keymanager.NewKeyManager(keymanager.WithAEAD(a), keymanager.WithStore(db), keymanager.WithSecrets(secrets)) 61 | if err != nil { 62 | panic(err) 63 | } 64 | 65 | kerl, err := keri.New(km, db) 66 | if err != nil { 67 | panic(err) 68 | } 69 | 70 | fmt.Printf("Direct Mode demo of Bob as %s on TCP port 5620 to port 5621\n\n\n", kerl.Prefix()) 71 | 72 | cli, err := direct.DialTimeout(kerl, ":5621", time.Duration(*e)*time.Second) 73 | if err != nil { 74 | panic(err) 75 | } 76 | 77 | msg, err := kerl.Inception() 78 | if err != nil { 79 | panic(err) 80 | } 81 | 82 | err = cli.WriteNotify(msg, func(rcpt *event.Event, err error) { 83 | if err != nil { 84 | panic(err) 85 | } 86 | 87 | rot, err := kerl.Rotate() 88 | if err != nil { 89 | panic(err) 90 | } 91 | 92 | err = cli.WriteNotify(rot, func(rcpt *event.Event, err error) { 93 | if err != nil { 94 | panic(err) 95 | } 96 | 97 | ixn, err := kerl.Interaction([]*event.Seal{}) 98 | if err != nil { 99 | panic(err) 100 | } 101 | 102 | err = cli.Write(ixn) 103 | if err != nil { 104 | panic(err) 105 | } 106 | 107 | }) 108 | if err != nil { 109 | panic(err) 110 | } 111 | }) 112 | 113 | select { 114 | case <-time.After(time.Duration(*e) * time.Second): 115 | os.Exit(0) 116 | } 117 | } 118 | 119 | func removeTempDir(td string) { 120 | err := os.RemoveAll(td) 121 | if err != nil { 122 | fmt.Println(err) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /pkg/event/attached-receipt.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | 8 | "github.com/pkg/errors" 9 | 10 | "github.com/decentralized-identity/kerigo/pkg/derivation" 11 | ) 12 | 13 | func ParseAttachedCouplets(buf io.Reader) ([]*Couplet, error) { 14 | out := []*Couplet{} 15 | 16 | rctCountBytes := make([]byte, 4) 17 | read, err := buf.Read(rctCountBytes) 18 | if read != 4 || err != nil { 19 | return nil, errors.New("invalid receipt count") 20 | } 21 | 22 | rctCount, err := derivation.Base64ToIndex(string(rctCountBytes)) 23 | if err != nil { 24 | return nil, fmt.Errorf("invalid receipt count (%s)", err) 25 | } 26 | 27 | // iterate over the receipt bytes for each receipt 28 | current := uint16(0) 29 | for current < rctCount { 30 | rct, err := ParseAttachedCouplet(buf) 31 | if err != nil { 32 | return nil, errors.Wrapf(err, "error parsing receipt %d", current) 33 | } 34 | 35 | out = append(out, rct) 36 | current++ 37 | } 38 | 39 | return out, nil 40 | } 41 | 42 | func ParseAttachedCouplet(r io.Reader) (*Couplet, error) { 43 | pre, err := derivation.ParsePrefix(r) 44 | if err != nil { 45 | return nil, errors.Wrap(err, "unable to read prefix from beginning of receipt") 46 | } 47 | 48 | sig, err := derivation.ParsePrefix(r) //This is a RCT, parse only the Signature remains 49 | if err != nil { 50 | return nil, errors.Wrap(err, "unable to read signature") 51 | } 52 | 53 | return &Couplet{ 54 | Prefix: pre, 55 | Signature: sig, 56 | }, nil 57 | 58 | } 59 | 60 | func ParseAttachedQuadlets(buf io.Reader) ([]*Quadlet, error) { 61 | out := []*Quadlet{} 62 | 63 | rctCountBytes := make([]byte, 4) 64 | read, err := buf.Read(rctCountBytes) 65 | if read != 4 || err != nil { 66 | return nil, errors.New("invalid receipt count") 67 | } 68 | 69 | rctCount, err := derivation.Base64ToIndex(string(rctCountBytes)) 70 | if err != nil { 71 | return nil, fmt.Errorf("invalid receipt count (%s)", err) 72 | } 73 | 74 | // iterate over the receipt bytes for each receipt 75 | current := uint16(0) 76 | for current < rctCount { 77 | rct, err := ParseAttachedQuadlet(buf) 78 | if err != nil { 79 | return nil, errors.Wrapf(err, "error parsing receipt %d", current) 80 | } 81 | 82 | out = append(out, rct) 83 | current++ 84 | } 85 | 86 | return out, nil 87 | } 88 | 89 | func ParseAttachedQuadlet(r io.Reader) (*Quadlet, error) { 90 | buf := bufio.NewReader(r) 91 | pre, err := derivation.ParsePrefix(buf) 92 | if err != nil { 93 | return nil, errors.Wrap(err, "unable to read prefix from beginning of receipt") 94 | } 95 | 96 | o, err := derivation.ParseOrdinal(buf) 97 | if err != nil { 98 | return nil, errors.Wrap(err, "unable to read establishment sequence number") 99 | } 100 | 101 | dig, err := derivation.ParsePrefix(buf) //This is a RCT, parse only the Signature remains 102 | if err != nil { 103 | return nil, errors.Wrap(err, "unable to read establishment digest") 104 | } 105 | 106 | sig, err := derivation.ParsePrefix(buf) 107 | if err != nil { 108 | return nil, errors.Wrap(err, "unable to read signature for receipt") 109 | } 110 | 111 | return &Quadlet{ 112 | Prefix: pre, 113 | Signature: sig, 114 | Digest: dig, 115 | Sequence: o.Num(), 116 | }, nil 117 | 118 | } 119 | -------------------------------------------------------------------------------- /pkg/encoding/stream/reader.go: -------------------------------------------------------------------------------- 1 | package stream 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "regexp" 8 | "strconv" 9 | "strings" 10 | 11 | "github.com/pkg/errors" 12 | 13 | "github.com/decentralized-identity/kerigo/pkg/derivation" 14 | "github.com/decentralized-identity/kerigo/pkg/event" 15 | ) 16 | 17 | const ( 18 | FullVerSize = 17 19 | 20 | MinSniffSize = 12 + FullVerSize 21 | 22 | Verex = `KERI(?P[0-9a-f])(?P[0-9a-f])(?P[A-Z]{4})(?P[0-9a-f]{6})_` 23 | ) 24 | 25 | var ( 26 | Rever = regexp.MustCompile(Verex) 27 | EOA = errors.New("EOA") 28 | ) 29 | 30 | type Reader struct { 31 | buf *bufio.Reader 32 | } 33 | 34 | func NewReader(r io.Reader) *Reader { 35 | return &Reader{ 36 | buf: bufio.NewReader(r), 37 | } 38 | } 39 | 40 | func (r *Reader) Read() (*event.Message, error) { 41 | 42 | // read a min sized buffer which contains the message length 43 | h, err := r.buf.Peek(MinSniffSize) 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | submatches := Rever.FindStringSubmatch(string(h)) 49 | if len(submatches) != 5 { 50 | return nil, errors.New("invalid version string") 51 | } 52 | 53 | ser := strings.TrimSpace(submatches[3]) 54 | hex := submatches[4] 55 | 56 | size, err := strconv.ParseInt(hex, 16, 64) 57 | if err != nil { 58 | return nil, errors.Wrap(err, "invalid message size hex") 59 | } 60 | 61 | f, err := event.Format(ser) 62 | if err != nil { 63 | return nil, err 64 | } 65 | 66 | buff := make([]byte, size) 67 | _, err = io.ReadFull(r.buf, buff) 68 | if err != nil { 69 | return nil, err 70 | } 71 | 72 | evt, err := event.Deserialize(buff, f) 73 | if err != nil { 74 | return nil, fmt.Errorf("unable to unmarshal event: (%v)", err) 75 | } 76 | 77 | opts := []event.MessageOption{} 78 | for { 79 | att, err := r.nextAttachment() 80 | if err == EOA { 81 | break 82 | } 83 | 84 | if err != nil { 85 | return nil, err 86 | } 87 | 88 | switch att.Code { 89 | case derivation.ControllerSigCountCode: 90 | opts = append(opts, event.WithSignatures(att.Signatures)) 91 | return event.NewMessage(evt, opts...) 92 | case derivation.WitnessSigCountCode: 93 | opts = append(opts, event.WithWitnessReceipts(att.WitnessReceipts)) 94 | case derivation.NonTransferableRctCountCode: 95 | opts = append(opts, event.WithNonTransferableReceipts(att.NonTransferableReceipts)) 96 | case derivation.TransferableRctCountCode: 97 | opts = append(opts, event.WithTransferableReceipts(att.TransferableReceipts)) 98 | } 99 | 100 | } 101 | 102 | return event.NewMessage(evt, opts...) 103 | } 104 | 105 | func (r *Reader) ReadAll() ([]*event.Message, error) { 106 | out := []*event.Message{} 107 | 108 | for { 109 | msg, err := r.Read() 110 | if err == io.EOF { 111 | break 112 | } 113 | 114 | if err != nil { 115 | return nil, err 116 | } 117 | 118 | out = append(out, msg) 119 | } 120 | 121 | return out, nil 122 | } 123 | 124 | func (r *Reader) nextAttachment() (*event.Attachment, error) { 125 | f, err := r.buf.Peek(1) 126 | if err == io.EOF { 127 | return nil, EOA 128 | } 129 | 130 | if err != nil { 131 | return nil, errors.Wrap(err, "error peeking") 132 | } 133 | 134 | typ := DetectFrameType(f) 135 | switch typ { 136 | case JSONFrame, MsgPackFrame, CBORFrame: 137 | return nil, EOA 138 | case QB64Frame: 139 | return event.ParseAttachment(r.buf) 140 | case QB2Frame: 141 | //TODO: apply binary translation here 142 | return event.ParseAttachment(r.buf) 143 | } 144 | 145 | return nil, errors.New("invalid stream state") 146 | } 147 | -------------------------------------------------------------------------------- /pkg/test/inception.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "crypto/ed25519" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | 9 | "github.com/decentralized-identity/kerigo/pkg/derivation" 10 | "github.com/decentralized-identity/kerigo/pkg/event" 11 | "github.com/decentralized-identity/kerigo/pkg/prefix" 12 | "github.com/decentralized-identity/kerigo/pkg/version" 13 | ) 14 | 15 | func InceptionFromSecrets(t *testing.T, keys, nexts []string, threshold, nextThreshold event.SigThreshold) *event.Event { 16 | var keyPres, nextPres []prefix.Prefix 17 | 18 | for _, k := range keys { 19 | kd, err := derivation.FromPrefix(k) 20 | if !assert.NoError(t, err) { 21 | return nil 22 | } 23 | edPriv := ed25519.NewKeyFromSeed(kd.Raw) 24 | keyDer, err := derivation.New(derivation.WithCode(derivation.Ed25519), derivation.WithRaw(edPriv.Public().(ed25519.PublicKey))) 25 | if !assert.NoError(t, err) { 26 | return nil 27 | } 28 | keyPres = append(keyPres, prefix.New(keyDer)) 29 | } 30 | 31 | for _, k := range nexts { 32 | kd, err := derivation.FromPrefix(k) 33 | if !assert.NoError(t, err) { 34 | return nil 35 | } 36 | edPriv := ed25519.NewKeyFromSeed(kd.Raw) 37 | keyDer, err := derivation.New(derivation.WithCode(derivation.Ed25519), derivation.WithRaw(edPriv.Public().(ed25519.PublicKey))) 38 | if !assert.NoError(t, err) { 39 | return nil 40 | } 41 | nextPres = append(nextPres, prefix.New(keyDer)) 42 | } 43 | 44 | icp, err := event.NewInceptionEvent( 45 | event.WithKeys(keyPres...), 46 | event.WithDefaultVersion(event.JSON), 47 | event.WithNext(nextThreshold.String(), derivation.Blake3256, nextPres...), 48 | ) 49 | if !assert.NoError(t, err) { 50 | return nil 51 | } 52 | icp.SigThreshold = &threshold 53 | 54 | icp.Prefix = derivation.Blake3256.Default() 55 | eventBytes, err := icp.Serialize() 56 | if !assert.NoError(t, err) { 57 | return nil 58 | } 59 | icp.Version = event.VersionString(event.JSON, version.Code(), len(eventBytes)) 60 | icp.Prefix = "" 61 | 62 | ser, err := icp.Serialize() 63 | if !assert.NoError(t, err) { 64 | return nil 65 | } 66 | 67 | saDerivation, _ := derivation.New(derivation.WithCode(derivation.Blake3256)) 68 | _, err = saDerivation.Derive(ser) 69 | if !assert.NoError(t, err) { 70 | return nil 71 | } 72 | 73 | selfAdd := prefix.New(saDerivation) 74 | icp.Prefix = selfAdd.String() 75 | 76 | return icp 77 | } 78 | 79 | func Inception(t *testing.T, edPub ed25519.PublicKey, nextPubDer *derivation.Derivation) *event.Event { 80 | keyDer, err := derivation.New(derivation.WithCode(derivation.Ed25519), derivation.WithRaw(edPub)) 81 | assert.NoError(t, err) 82 | 83 | keyPre := prefix.New(keyDer) 84 | 85 | nextKeyPre := prefix.New(nextPubDer) 86 | 87 | icp, err := event.NewInceptionEvent(event.WithKeys(keyPre), event.WithDefaultVersion(event.JSON), event.WithNext("1", derivation.Blake3256, nextKeyPre)) 88 | assert.NoError(t, err) 89 | 90 | // Serialize with defaults to get correct length for version string 91 | icp.Prefix = derivation.Blake3256.Default() 92 | icp.Version = event.DefaultVersionString(event.JSON) 93 | eventBytes, err := event.Serialize(icp, event.JSON) 94 | assert.NoError(t, err) 95 | 96 | eventBytesExpected := len(eventBytes) 97 | icp.Version = event.VersionString(event.JSON, version.Code(), len(eventBytes)) 98 | icp.Prefix = "" 99 | 100 | ser, err := event.Serialize(icp, event.JSON) 101 | assert.NoError(t, err) 102 | 103 | saDerivation, err := derivation.New(derivation.WithCode(derivation.Blake3256)) 104 | assert.NoError(t, err) 105 | 106 | _, err = saDerivation.Derive(ser) 107 | assert.NoError(t, err) 108 | 109 | selfAdd := prefix.New(saDerivation) 110 | assert.NoError(t, err) 111 | selfAddAID := selfAdd.String() 112 | assert.Nil(t, err) 113 | 114 | // Set as the prefix for the inception event 115 | icp.Prefix = selfAddAID 116 | 117 | eventBytes, err = event.Serialize(icp, event.JSON) 118 | assert.NoError(t, err) 119 | assert.Equal(t, eventBytesExpected, len(eventBytes)) 120 | 121 | return icp 122 | } 123 | -------------------------------------------------------------------------------- /pkg/event/receipt_test.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "crypto/ed25519" 5 | "encoding/json" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | 10 | "github.com/decentralized-identity/kerigo/pkg/derivation" 11 | "github.com/decentralized-identity/kerigo/pkg/prefix" 12 | "github.com/decentralized-identity/kerigo/pkg/version" 13 | ) 14 | 15 | func TestTransferable(t *testing.T) { 16 | remoteSecret := "ArwXoACJgOleVZ2PY7kXn7rA0II0mHYDhc6WrBH8fDAc" 17 | remoteNext := "A6zz7M08-HQSFq92sJ8KJOT2cZ47x7pXFQLPB0pckB3Q" 18 | //localSecret := "AgjD4nRlycmM5cPcAkfOATAp8wVldRsnc9f1tiwctXlw" 19 | //localNext := "AKUotEE0eAheKdDJh9QvNmSEmO_bjIav8V_GmctGpuCQ" 20 | 21 | remoteICP := incept(t, remoteSecret, remoteNext) 22 | //estEvent := incept(t, localSecret, localNext) 23 | 24 | icpBytes := `{"v":"KERI10JSON0000e6_","i":"Ep9IFLmnLTwz_EfZCXOuVHcYFmoHNKgqz7nQ1ItKX9pc","s":"0","t":"icp","kt":"1","k":["DSuhyBcPZEZLK-fcw5tzHn2N46wRCG_ZOoeKtWTOunRA"],"n":"EPYuj8mq_PYYsoBKkzX1kxSPGYBWaIya3slgCOyOtlqU","wt":"0","w":[],"c":[]}` 25 | //expectedVRCBytes := `{"v":"KERI10JSON000105_","i":"Ep9IFLmnLTwz_EfZCXOuVHcYFmoHNKgqz7nQ1ItKX9pc","s":"0","t":"vrc","d":"EBSQD8MrJi-qTF--fg1hMT7a-sVacyFjeaPn3FduKNsc","a":{"i":"E482bsaPDuLO25ilSJkErz-Xqmw4knyAZd1Ah01do9k0","s":"0","d":"Ej2wcLnGA6DJHhF3f08nIIhoZncG2O1pVKgFvWLPDFjg"}}` 26 | 27 | d, _ := json.Marshal(remoteICP) 28 | assert.JSONEq(t, icpBytes, string(d)) 29 | 30 | } 31 | 32 | func TestNonTransferable(t *testing.T) { 33 | remoteSecret := "ArwXoACJgOleVZ2PY7kXn7rA0II0mHYDhc6WrBH8fDAc" 34 | remoteNext := "A6zz7M08-HQSFq92sJ8KJOT2cZ47x7pXFQLPB0pckB3Q" 35 | 36 | remoteICP := incept(t, remoteSecret, remoteNext) 37 | 38 | icpBytes := `{"v":"KERI10JSON0000e6_","i":"Ep9IFLmnLTwz_EfZCXOuVHcYFmoHNKgqz7nQ1ItKX9pc","s":"0","t":"icp","kt":"1","k":["DSuhyBcPZEZLK-fcw5tzHn2N46wRCG_ZOoeKtWTOunRA"],"n":"EPYuj8mq_PYYsoBKkzX1kxSPGYBWaIya3slgCOyOtlqU","wt":"0","w":[],"c":[]}` 39 | //expectedVRCBytes := `{"v":"KERI10JSON0000a3_","i":"Ep9IFLmnLTwz_EfZCXOuVHcYFmoHNKgqz7nQ1ItKX9pc","s":"0","t":"rct","d":"EBSQD8MrJi-qTF--fg1hMT7a-sVacyFjeaPn3FduKNsc","kt":"1","wt":"0"}` 40 | 41 | d, _ := json.Marshal(remoteICP) 42 | assert.JSONEq(t, icpBytes, string(d)) 43 | 44 | } 45 | 46 | func incept(t *testing.T, secret, next string) *Event { 47 | der, err := derivation.FromPrefix(secret) 48 | assert.NoError(t, err) 49 | 50 | edPriv := ed25519.NewKeyFromSeed(der.Raw) 51 | edPub := edPriv.Public() 52 | keyDer, err := derivation.New(derivation.WithCode(derivation.Ed25519), derivation.WithRaw(edPub.(ed25519.PublicKey))) 53 | assert.NoError(t, err) 54 | keyPre := prefix.New(keyDer) 55 | 56 | nextder, err := derivation.FromPrefix(next) 57 | assert.NoError(t, err) 58 | 59 | nextPriv := ed25519.NewKeyFromSeed(nextder.Raw) 60 | nextPub := nextPriv.Public() 61 | nextPubDer, err := derivation.New(derivation.WithCode(derivation.Ed25519), derivation.WithRaw(nextPub.(ed25519.PublicKey))) 62 | assert.NoError(t, err) 63 | 64 | nextKeyPre := prefix.New(nextPubDer) 65 | 66 | icp, err := NewInceptionEvent(WithKeys(keyPre), WithDefaultVersion(JSON), WithNext("1", derivation.Blake3256, nextKeyPre)) 67 | assert.NoError(t, err) 68 | 69 | // Serialize with defaults to get correct length for version string 70 | icp.Prefix = derivation.Blake3256.Default() 71 | icp.Version = DefaultVersionString(JSON) 72 | eventBytes, err := Serialize(icp, JSON) 73 | assert.NoError(t, err) 74 | 75 | eventBytesExpected := len(eventBytes) 76 | icp.Version = VersionString(JSON, version.Code(), len(eventBytes)) 77 | icp.Prefix = "" 78 | 79 | ser, err := icp.extractDataSet() 80 | 81 | saDerivation, err := derivation.New(derivation.WithCode(derivation.Blake3256)) 82 | assert.NoError(t, err) 83 | 84 | _, err = saDerivation.Derive(ser) 85 | assert.NoError(t, err) 86 | 87 | selfAdd := prefix.New(saDerivation) 88 | assert.NoError(t, err) 89 | selfAddAID := selfAdd.String() 90 | assert.Nil(t, err) 91 | 92 | // Set as the prefix for the inception event 93 | icp.Prefix = selfAddAID 94 | 95 | eventBytes, err = Serialize(icp, JSON) 96 | assert.Equal(t, eventBytesExpected, len(eventBytes)) 97 | 98 | return icp 99 | } 100 | -------------------------------------------------------------------------------- /pkg/event/seal.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "strconv" 8 | 9 | "github.com/decentralized-identity/kerigo/pkg/derivation" 10 | ) 11 | 12 | type SealType int 13 | 14 | const ( 15 | DigestSeal SealType = iota 16 | RootSeal 17 | EventSeal 18 | EventLocationSeal 19 | ) 20 | 21 | type SealOption func(*Seal) error 22 | 23 | // Seal is used to anchor particular data to an event 24 | // There are multiple types of seals, each with 25 | // a different combination of data points. 26 | type Seal struct { 27 | Type SealType `json:"-"` 28 | Root string `json:"rd,omitempty"` 29 | Prefix string `json:"i,omitempty"` 30 | Sequence string `json:"s,omitempty"` 31 | EventType string `json:"t,omitempty"` 32 | Digest string `json:"d,omitempty"` 33 | } 34 | 35 | func NewSeal(typ SealType, opts ...SealOption) (*Seal, error) { 36 | s := &Seal{ 37 | Type: typ, 38 | } 39 | 40 | for _, o := range opts { 41 | err := o(s) 42 | if err != nil { 43 | return nil, err 44 | } 45 | } 46 | 47 | return s, nil 48 | } 49 | 50 | func NewDigestSeal(dig string) (*Seal, error) { 51 | return NewSeal(DigestSeal, WithSealDigest(dig)) 52 | } 53 | 54 | func NewRootSeal(rt string) (*Seal, error) { 55 | return NewSeal(RootSeal, WithRoot(rt)) 56 | } 57 | 58 | func NewEventSeal(dig, pre, sn string) (*Seal, error) { 59 | return NewSeal(EventSeal, WithSealDigest(dig), WithSealPrefix(pre), WithSealSequence(sn)) 60 | } 61 | 62 | func SealEstablishment(evt *Event) (*Seal, error) { 63 | ser, err := evt.Serialize() 64 | if err != nil { 65 | return nil, fmt.Errorf("error serializing establshment event to extracted data set: %v", err) 66 | } 67 | 68 | sealDigest, err := DigestString(ser, derivation.Blake3256) 69 | if err != nil { 70 | return nil, fmt.Errorf("unable to digest establishment event: %v", err) 71 | } 72 | 73 | s, err := NewEventSeal(sealDigest, evt.Prefix, evt.Sequence) 74 | if err != nil { 75 | return nil, fmt.Errorf("unable to create last est evt seal for receipt: %v", err) 76 | } 77 | 78 | return s, nil 79 | } 80 | 81 | func NewEventLocationSeal(dig, pre, sn string, ilk ILK) (*Seal, error) { 82 | return NewSeal(EventLocationSeal, 83 | WithSealDigest(dig), 84 | WithSealPrefix(pre), 85 | WithSealSequence(sn), 86 | WithSealEventType(ilk), 87 | ) 88 | } 89 | 90 | func WithSealDigest(dig string) SealOption { 91 | return func(s *Seal) error { 92 | s.Digest = dig 93 | return nil 94 | } 95 | } 96 | 97 | func WithRoot(rt string) SealOption { 98 | return func(s *Seal) error { 99 | s.Root = rt 100 | return nil 101 | } 102 | } 103 | 104 | func WithSealPrefix(pre string) SealOption { 105 | return func(s *Seal) error { 106 | s.Prefix = pre 107 | return nil 108 | } 109 | } 110 | 111 | func WithSealEventType(eventType ILK) SealOption { 112 | return func(e *Seal) error { 113 | e.EventType = ilkString[eventType] 114 | return nil 115 | } 116 | } 117 | 118 | func WithSealSequence(sn string) SealOption { 119 | return func(s *Seal) error { 120 | s.Sequence = sn 121 | return nil 122 | } 123 | } 124 | 125 | type SealArray []*Seal 126 | 127 | func (r *SealArray) UnmarshalJSON(b []byte) error { 128 | a := []*Seal(*r) 129 | if len(b) == 0 { 130 | *r = nil 131 | return nil 132 | } 133 | 134 | if b[0] == '[' { 135 | err := json.Unmarshal(b, &a) 136 | if err != nil { 137 | return err 138 | } 139 | 140 | *r = a 141 | return nil 142 | } else if b[0] == '{' { 143 | s := &Seal{} 144 | err := json.Unmarshal(b, s) 145 | if err != nil { 146 | return err 147 | } 148 | 149 | *r = []*Seal{s} 150 | 151 | return nil 152 | } else { 153 | return errors.New("unmarshal of Seal Array") 154 | } 155 | } 156 | 157 | func (r *SealArray) MarshalJSON() ([]byte, error) { 158 | a := []*Seal(*r) 159 | if len(a) == 1 { 160 | return json.Marshal(a[0]) 161 | } 162 | 163 | return json.Marshal(a) 164 | } 165 | 166 | // SequenceInt returns an integer representation of the 167 | // hex sequence string 168 | func (r *Seal) SequenceInt() int { 169 | eInt, err := strconv.ParseInt(r.Sequence, 16, 64) 170 | if err != nil { 171 | return -1 172 | } 173 | return int(eInt) 174 | } 175 | -------------------------------------------------------------------------------- /pkg/db/badger/vals.go: -------------------------------------------------------------------------------- 1 | package badger 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/dgraph-io/badger" 9 | "github.com/pkg/errors" 10 | ) 11 | 12 | func addVals(txn *badger.Txn, k string, vals [][]byte) error { 13 | cur, err := getVals(txn, k) 14 | if err != nil { 15 | return err 16 | } 17 | 18 | s := set(cur) 19 | 20 | for _, val := range vals { 21 | s[string(val)] = true 22 | } 23 | 24 | i := 0 25 | for v := range s { 26 | ik := fmt.Sprintf("%s%032x", k, i) 27 | err := txn.Set([]byte(ik), []byte(v)) 28 | if err != nil { 29 | return err 30 | } 31 | i++ 32 | } 33 | return nil 34 | } 35 | 36 | func delVals(txn *badger.Txn, k string) error { 37 | opts := badger.DefaultIteratorOptions 38 | opts.Prefix = []byte(k) 39 | it := txn.NewIterator(opts) 40 | defer it.Close() 41 | 42 | for it.Rewind(); it.Valid(); it.Next() { 43 | key := it.Item().KeyCopy(nil) 44 | err := txn.Delete(key) 45 | if err != nil { 46 | return err 47 | } 48 | } 49 | 50 | return nil 51 | } 52 | 53 | func removeFromVals(txn *badger.Txn, k string, v []byte) error { 54 | opts := badger.DefaultIteratorOptions 55 | opts.Prefix = []byte(k) 56 | it := txn.NewIterator(opts) 57 | defer it.Close() 58 | 59 | for it.Rewind(); it.Valid(); it.Next() { 60 | key := it.Item().KeyCopy(nil) 61 | val, _ := it.Item().ValueCopy(nil) 62 | if bytes.Compare(val, v) == 0 { 63 | err := txn.Delete(key) 64 | if err != nil { 65 | return err 66 | } 67 | break 68 | } 69 | } 70 | 71 | return nil 72 | } 73 | 74 | func countVals(txn *badger.Txn, k string) int { 75 | opts := badger.DefaultIteratorOptions 76 | opts.Prefix = []byte(k) 77 | it := txn.NewIterator(opts) 78 | defer it.Close() 79 | 80 | count := 0 81 | for it.Rewind(); it.Valid(); it.Next() { 82 | count++ 83 | } 84 | 85 | return count 86 | } 87 | 88 | func getVals(txn *badger.Txn, k string) ([][]byte, error) { 89 | var vals [][]byte 90 | 91 | opts := badger.DefaultIteratorOptions 92 | opts.Prefix = []byte(k) 93 | it := txn.NewIterator(opts) 94 | defer it.Close() 95 | 96 | for it.Seek([]byte(k)); it.ValidForPrefix([]byte(k)); it.Next() { 97 | item := it.Item() 98 | v, _ := item.ValueCopy(nil) 99 | vals = append(vals, v) 100 | } 101 | return vals, nil 102 | } 103 | 104 | func set(vals [][]byte) map[string]bool { 105 | out := map[string]bool{} 106 | for _, val := range vals { 107 | out[string(val)] = true 108 | } 109 | return out 110 | } 111 | 112 | func addOrderedVals(txn *badger.Txn, k string, vals [][]byte) error { 113 | cur, err := getOrderedVals(txn, k) 114 | if err != nil { 115 | return err 116 | } 117 | 118 | s := set(cur) 119 | 120 | for _, val := range vals { 121 | s[string(val)] = true 122 | } 123 | 124 | all := append(cur, vals...) 125 | 126 | i := 0 127 | for _, v := range all { 128 | if _, ok := s[string(v)]; !ok { 129 | continue 130 | } 131 | 132 | delete(s, string(v)) 133 | ik := fmt.Sprintf("%s%032x", k, i) 134 | err := txn.Set([]byte(ik), v) 135 | if err != nil { 136 | return err 137 | } 138 | i++ 139 | } 140 | return nil 141 | } 142 | 143 | func getOrderedVals(txn *badger.Txn, k string) ([][]byte, error) { 144 | var vals [][]byte 145 | 146 | opts := badger.DefaultIteratorOptions 147 | it := txn.NewIterator(opts) 148 | defer it.Close() 149 | 150 | for it.Seek([]byte(k)); it.ValidForPrefix([]byte(k)); it.Next() { 151 | item := it.Item() 152 | v, _ := item.ValueCopy(nil) 153 | vals = append(vals, v) 154 | } 155 | 156 | return vals, nil 157 | } 158 | 159 | func iteratorFromKeyCode(keyCode string) string { 160 | 161 | i := strings.LastIndexByte(keyCode, '/') 162 | if i == -1 { 163 | return keyCode 164 | } 165 | 166 | return keyCode[:i] 167 | } 168 | 169 | func distalVal(txn *badger.Txn, seek string, reverse bool) ([]byte, error) { 170 | 171 | opts := badger.DefaultIteratorOptions 172 | opts.Reverse = reverse 173 | 174 | it := txn.NewIterator(opts) 175 | defer it.Close() 176 | 177 | if reverse { 178 | seek += "~" 179 | } 180 | 181 | it.Rewind() 182 | it.Seek([]byte(seek)) 183 | if !it.Valid() { 184 | return nil, errors.New("not found") 185 | } 186 | 187 | item := it.Item() 188 | out, err := item.ValueCopy(nil) 189 | if err != nil { 190 | return nil, err 191 | } 192 | 193 | return out, nil 194 | } 195 | -------------------------------------------------------------------------------- /pkg/encoding/stream/serialize.go: -------------------------------------------------------------------------------- 1 | package stream 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/decentralized-identity/kerigo/pkg/derivation" 8 | "github.com/decentralized-identity/kerigo/pkg/event" 9 | ) 10 | 11 | type ReplayMode int 12 | 13 | const ( 14 | DisjointMode ReplayMode = iota 15 | ConjointMode 16 | ) 17 | 18 | func ToDisjoint(m *event.Message) ([]byte, error) { 19 | evt, err := m.Event.Serialize() 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | sc, err := derivation.NewSigCounter(derivation.ControllerSigCountCode, derivation.WithCount(len(m.Signatures))) 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | cntCode, err := sc.String() 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | evt = append(evt, cntCode...) 35 | for _, sig := range m.Signatures { 36 | evt = append(evt, sig.AsPrefix()...) 37 | } 38 | 39 | for _, rcpt := range m.TransferableReceipts { 40 | msg, err := rcpt.Message() 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | d, err := ToDisjoint(msg) 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | evt = append(evt, d...) 51 | } 52 | 53 | for _, rcpt := range m.NonTransferableReceipts { 54 | msg, err := rcpt.Message() 55 | if err != nil { 56 | return nil, err 57 | } 58 | 59 | d, err := ToDisjoint(msg) 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | evt = append(evt, d...) 65 | } 66 | 67 | for _, rcpt := range m.WitnessReceipts { 68 | msg, err := rcpt.Message() 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | d, err := ToDisjoint(msg) 74 | if err != nil { 75 | return nil, err 76 | } 77 | 78 | evt = append(evt, d...) 79 | } 80 | 81 | return evt, nil 82 | } 83 | 84 | func ToConjoint(m *event.Message) ([]byte, error) { 85 | 86 | switch m.Event.ILK() { 87 | case event.VRC: 88 | return conjointVRC(m) 89 | case event.RCT: 90 | return conjointRCT(m) 91 | default: 92 | return conjoint(m) 93 | } 94 | } 95 | 96 | func conjoint(m *event.Message) ([]byte, error) { 97 | 98 | evt, err := m.Event.Serialize() 99 | if err != nil { 100 | return nil, err 101 | } 102 | 103 | sc, err := derivation.NewSigCounter(derivation.ControllerSigCountCode, derivation.WithCount(len(m.Signatures))) 104 | if err != nil { 105 | return nil, err 106 | } 107 | 108 | cntCode, err := sc.String() 109 | if err != nil { 110 | return nil, err 111 | } 112 | 113 | evt = append(evt, cntCode...) 114 | for _, sig := range m.Signatures { 115 | evt = append(evt, sig.AsPrefix()...) 116 | } 117 | 118 | if len(m.TransferableReceipts) > 0 { 119 | sc, err = derivation.NewSigCounter(derivation.TransferableRctCountCode, derivation.WithCount(len(m.TransferableReceipts))) 120 | if err != nil { 121 | return nil, err 122 | } 123 | 124 | cntCode, err = sc.String() 125 | if err != nil { 126 | return nil, err 127 | } 128 | 129 | evt = append(evt, cntCode...) 130 | for _, rcpt := range m.TransferableReceipts { 131 | evt = append(evt, rcpt.Text()...) 132 | } 133 | } 134 | 135 | if len(m.NonTransferableReceipts) > 0 { 136 | sc, err = derivation.NewSigCounter(derivation.NonTransferableRctCountCode, derivation.WithCount(len(m.TransferableReceipts))) 137 | if err != nil { 138 | return nil, err 139 | } 140 | 141 | cntCode, err = sc.String() 142 | if err != nil { 143 | return nil, err 144 | } 145 | 146 | evt = append(evt, cntCode...) 147 | for _, rcpt := range m.NonTransferableReceipts { 148 | evt = append(evt, rcpt.Text()...) 149 | } 150 | } 151 | return evt, nil 152 | } 153 | 154 | func conjointVRC(m *event.Message) ([]byte, error) { 155 | //Transferable identifier prefix, the latest establishment event sequence number, 156 | // the latest establishment event digest and the associated signature 157 | // Those are the validator prefix and the signature attached to the RCT 158 | 159 | seal := m.Event.Seals[0] 160 | sig := m.Signatures[0] 161 | 162 | quadlet := strings.Join([]string{seal.Prefix, fmt.Sprintf("%024d", seal.SequenceInt()), seal.Digest, sig.AsPrefix()}, "") 163 | 164 | return []byte(quadlet), nil 165 | } 166 | 167 | func conjointRCT(m *event.Message) ([]byte, error) { 168 | // Witness identifier prefix and the associated signature 169 | // Those are the validator prefix and the signature attached to the RCT 170 | pre := m.Event.Prefix 171 | sig := m.Signatures[0] 172 | 173 | couplet := strings.Join([]string{pre, sig.AsPrefix()}, "") 174 | 175 | return []byte(couplet), nil 176 | } 177 | -------------------------------------------------------------------------------- /pkg/log/escrow_test.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/decentralized-identity/kerigo/pkg/derivation" 7 | "github.com/decentralized-identity/kerigo/pkg/event" 8 | "github.com/decentralized-identity/kerigo/pkg/prefix" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestEscrow(t *testing.T) { 13 | assert := assert.New(t) 14 | 15 | esc := Escrow(map[string]*event.Message{}) 16 | 17 | keys, err := newKeys(3) 18 | if !assert.Nil(err) { 19 | return 20 | } 21 | prefixes := []prefix.Prefix{} 22 | for _, k := range keys { 23 | prefixes = append(prefixes, k.pre) 24 | } 25 | 26 | // Create an event and add a signature 27 | icp, err := event.NewInceptionEvent(event.WithDefaultVersion(event.JSON), event.WithKeys(prefixes...)) 28 | assert.Nil(err) 29 | 30 | sig, err := derivation.New(derivation.WithCode(derivation.Ed25519Attached)) 31 | if !assert.Nil(err) { 32 | return 33 | } 34 | sig.KeyIndex = 0 35 | 36 | err = esc.Add(&event.Message{Event: icp, Signatures: []derivation.Derivation{*sig}}) 37 | assert.Nil(err) 38 | assert.Len(esc, 1) 39 | 40 | // Applying the same event should not increase escrow size or signature size 41 | err = esc.Add(&event.Message{Event: icp, Signatures: []derivation.Derivation{*sig}}) 42 | assert.Nil(err) 43 | if assert.Len(esc, 1) { 44 | m, err := esc.Get(icp) 45 | assert.Nil(err) 46 | assert.Equal(icp, m.Event) 47 | assert.Len(m.Signatures, 1) 48 | } 49 | 50 | sig2, err := derivation.New(derivation.WithCode(derivation.Ed25519Attached)) 51 | if !assert.Nil(err) { 52 | return 53 | } 54 | sig2.KeyIndex = 1 55 | 56 | // applying the same message again, but with a different signature, should add the sig 57 | err = esc.Add(&event.Message{Event: icp, Signatures: []derivation.Derivation{*sig2}}) 58 | assert.Nil(err) 59 | if assert.Len(esc, 1) { 60 | m, err := esc.Get(icp) 61 | assert.Nil(err) 62 | assert.Equal(icp, m.Event) 63 | assert.Len(m.Signatures, 2) 64 | } 65 | 66 | // create another event with different keys (though same in other respects) 67 | keys2, err := newKeys(3) 68 | if !assert.Nil(err) { 69 | return 70 | } 71 | prefixes2 := []prefix.Prefix{} 72 | for _, k := range keys2 { 73 | prefixes2 = append(prefixes2, k.pre) 74 | } 75 | 76 | icp2, err := event.NewInceptionEvent(event.WithDefaultVersion(event.JSON), event.WithKeys(prefixes2...)) 77 | assert.Nil(err) 78 | 79 | // adding another event - same type, seq, etc, but different keys, so this is a "different" event. 80 | // Escrow should maintain them separately 81 | err = esc.Add(&event.Message{Event: icp2, Signatures: []derivation.Derivation{*sig}}) 82 | assert.Nil(err) 83 | if assert.Len(esc, 2) { 84 | m, err := esc.Get(icp2) 85 | assert.Equal(icp2, m.Event) 86 | assert.Nil(err) 87 | assert.Len(m.Signatures, 1) 88 | } 89 | 90 | // Add several more events to the escrow 91 | ixn, err := event.NewEvent( 92 | event.WithKeys(prefixes...), 93 | event.WithSequence(1), 94 | event.WithType(event.IXN), 95 | event.WithDefaultVersion(event.JSON), 96 | ) 97 | assert.Nil(err) 98 | 99 | err = esc.Add(&event.Message{Event: ixn, Signatures: []derivation.Derivation{*sig}}) 100 | assert.Nil(err) 101 | assert.Len(esc, 3) 102 | 103 | ixn, err = event.NewEvent( 104 | event.WithKeys(prefixes...), 105 | event.WithSequence(2), 106 | event.WithType(event.IXN), 107 | event.WithDefaultVersion(event.JSON), 108 | ) 109 | assert.Nil(err) 110 | 111 | err = esc.Add(&event.Message{Event: ixn, Signatures: []derivation.Derivation{*sig}}) 112 | assert.Nil(err) 113 | assert.Len(esc, 4) 114 | 115 | ixn, err = event.NewEvent( 116 | event.WithKeys(prefixes...), 117 | event.WithSequence(3), 118 | event.WithType(event.IXN), 119 | event.WithDefaultVersion(event.JSON), 120 | ) 121 | assert.Nil(err) 122 | 123 | err = esc.Add(&event.Message{Event: ixn, Signatures: []derivation.Derivation{*sig}}) 124 | assert.Nil(err) 125 | assert.Len(esc, 5) 126 | 127 | // get the events for the sequence 128 | msgs := esc.ForSequence(3) 129 | if assert.Len(msgs, 1) { 130 | assert.Equal(msgs[0].Event, ixn) 131 | assert.Len(msgs[0].Signatures, 1) 132 | } 133 | 134 | msgs = esc.ForSequence(0) 135 | assert.Len(msgs, 2) 136 | 137 | // Clear events 138 | _, err = esc.Clear(*ixn) 139 | assert.Nil(err) 140 | assert.Len(esc, 4) 141 | msgs = esc.ForSequence(3) 142 | assert.Empty(msgs) 143 | 144 | // this should return 145 | leftovers, err := esc.Clear(*icp) 146 | assert.Nil(err) 147 | if assert.Len(leftovers, 1) { 148 | assert.Equal(leftovers[0].Event, icp2) 149 | } 150 | assert.Len(esc, 2) 151 | msgs = esc.ForSequence(0) 152 | assert.Empty(msgs) 153 | } 154 | -------------------------------------------------------------------------------- /pkg/event/main_test.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "crypto/ed25519" 5 | "crypto/rand" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | 10 | "github.com/decentralized-identity/kerigo/pkg/derivation" 11 | "github.com/decentralized-identity/kerigo/pkg/prefix" 12 | ) 13 | 14 | func TestNewInceptionEvent(t *testing.T) { 15 | assert := assert.New(t) 16 | 17 | // ed25519 18 | edPub, _, err := ed25519.GenerateKey(rand.Reader) 19 | if !assert.Nil(err) { 20 | return 21 | } 22 | 23 | basicDerivation, err := derivation.New(derivation.WithCode(derivation.Ed25519), derivation.WithRaw(edPub)) 24 | assert.Nil(err) 25 | 26 | basicPre := prefix.New(basicDerivation) 27 | 28 | icp, err := NewInceptionEvent(WithKeys(basicPre)) 29 | assert.Nil(err) 30 | if assert.Len(icp.Keys, 1) { 31 | basicPreAID := basicPre.String() 32 | assert.Contains(icp.Keys, basicPreAID) 33 | } 34 | 35 | } 36 | 37 | func TestNext(t *testing.T) { 38 | assert := assert.New(t) 39 | 40 | d1, _ := derivation.FromPrefix("BrHLayDN-mXKv62DAjFLX1_Y5yEUe0vA9YPe_ihiKYHE") 41 | d1p := prefix.New(d1) 42 | d2, _ := derivation.FromPrefix("BujP_71bmWFVcvFmkE9uS8BTZ54GIstZ20nj_UloF8Rk") 43 | d2p := prefix.New(d2) 44 | d3, _ := derivation.FromPrefix("B8T4xkb8En6o0Uo5ZImco1_08gT5zcYnXzizUPVNzicw") 45 | d3p := prefix.New(d3) 46 | 47 | event, err := NewEvent(WithType(ICP), WithNext("2", derivation.Blake3256, d1p, d2p, d3p)) 48 | assert.Nil(err) 49 | assert.Equal("ED8YvDrXvGuaIVZ69XsBVA5YN2pNTfQOFwgeloVHeWKs", event.Next) 50 | 51 | //test case from Bob demo in python 52 | der, err := derivation.FromPrefix("A6zz7M08-HQSFq92sJ8KJOT2cZ47x7pXFQLPB0pckB3Q") 53 | assert.NoError(err) 54 | edPriv := ed25519.NewKeyFromSeed(der.Raw) 55 | edPub := edPriv.Public() 56 | 57 | basicDerivation, err := derivation.New(derivation.WithCode(derivation.Ed25519), derivation.WithRaw(edPub.(ed25519.PublicKey))) 58 | basicPre := prefix.New(basicDerivation) 59 | 60 | event, err = NewEvent(WithType(ICP), WithNext("1", derivation.Blake3256, basicPre)) 61 | assert.NoError(err) 62 | assert.Equal("EPYuj8mq_PYYsoBKkzX1kxSPGYBWaIya3slgCOyOtlqU", event.Next) 63 | 64 | } 65 | 66 | func TestGetDigest(t *testing.T) { 67 | 68 | icp := incept(t, "ADW3o9m3udwEf0aoOdZLLJdf1aylokP0lwwI_M2J9h0s", "AagumsL8FeGES7tYcnr_5oN6qcwJzZfLKxoniKUpG4qc") 69 | 70 | dig, err := icp.GetDigest() 71 | assert.NoError(t, err) 72 | assert.Equal(t, "EeM1ZikRHU9XKxd3pQrjLOPyP8bQkQQriYBk-_UYpQfE", dig) 73 | 74 | } 75 | 76 | func TestRotationEvent(t *testing.T) { 77 | t.Run("happy", func(t *testing.T) { 78 | expectedRotBytes := `{"v":"KERI10JSON0000ba_","i":"Efxqin4pHh--KbxFN7xcOnOakf2CAK19zknumybXxabI","s":"0","t":"rot","kt":"1","n":"EOF414QEuea9A-Svo-tzipeVfk0-DvtAsaLULWCnHXw4","wt":"0","wr":[],"wa":[],"a":[]}` 79 | 80 | icp := incept(t, "ADW3o9m3udwEf0aoOdZLLJdf1aylokP0lwwI_M2J9h0s", "AagumsL8FeGES7tYcnr_5oN6qcwJzZfLKxoniKUpG4qc") 81 | 82 | der, err := derivation.FromPrefix("Ap5waegfnuP6ezC18w7jQiPyQwYYsp9Yv9rYMlKAYL8k") 83 | assert.NoError(t, err) 84 | 85 | edPriv := ed25519.NewKeyFromSeed(der.Raw) 86 | edPub := edPriv.Public() 87 | keyDer, err := derivation.New(derivation.WithCode(derivation.Ed25519), derivation.WithRaw(edPub.(ed25519.PublicKey))) 88 | assert.NoError(t, err) 89 | nextPre := prefix.New(keyDer) 90 | 91 | evt, err := NewRotationEvent(WithPrefix(icp.Prefix), WithNext("1", derivation.Blake3256, nextPre)) 92 | assert.NoError(t, err) 93 | 94 | b, err := evt.Serialize() 95 | assert.NoError(t, err) 96 | 97 | assert.Equal(t, expectedRotBytes, string(b)) 98 | }) 99 | t.Run("invalid rotation prefix", func(t *testing.T) { 100 | evt, err := NewRotationEvent() 101 | assert.Error(t, err) 102 | assert.Nil(t, evt) 103 | assert.Equal(t, "prefix required for rot", err.Error()) 104 | }) 105 | t.Run("invalid rotation next commitment", func(t *testing.T) { 106 | evt, err := NewRotationEvent(WithPrefix("Eh0fefvTQ55Jwps4dVnIekf7mZgWoU8bCUsDsKeGiEgU")) 107 | assert.Error(t, err) 108 | assert.Nil(t, evt) 109 | assert.Equal(t, "next commitment required for rot", err.Error()) 110 | }) 111 | } 112 | 113 | func TestInteractionEvent(t *testing.T) { 114 | t.Run("happy", func(t *testing.T) { 115 | expectedRotBytes := `{"v":"KERI10JSON000065_","i":"Efxqin4pHh--KbxFN7xcOnOakf2CAK19zknumybXxabI","s":"1","t":"ixn","a":[]}` 116 | 117 | icp := incept(t, "ADW3o9m3udwEf0aoOdZLLJdf1aylokP0lwwI_M2J9h0s", "AagumsL8FeGES7tYcnr_5oN6qcwJzZfLKxoniKUpG4qc") 118 | 119 | evt, err := NewInteractionEvent(WithPrefix(icp.Prefix), WithSequence(1)) 120 | assert.NoError(t, err) 121 | 122 | b, err := evt.Serialize() 123 | assert.NoError(t, err) 124 | 125 | assert.Equal(t, expectedRotBytes, string(b)) 126 | }) 127 | } 128 | -------------------------------------------------------------------------------- /pkg/direct/server.go: -------------------------------------------------------------------------------- 1 | package direct 2 | 3 | import ( 4 | "log" 5 | "net" 6 | "sync" 7 | 8 | "github.com/pkg/errors" 9 | 10 | "github.com/decentralized-identity/kerigo/pkg/db" 11 | "github.com/decentralized-identity/kerigo/pkg/db/mem" 12 | "github.com/decentralized-identity/kerigo/pkg/encoding/stream" 13 | "github.com/decentralized-identity/kerigo/pkg/event" 14 | "github.com/decentralized-identity/kerigo/pkg/keri" 15 | "github.com/decentralized-identity/kerigo/pkg/keymanager" 16 | ) 17 | 18 | const ( 19 | DefaultDirectModeAddr = ":5620" 20 | ) 21 | 22 | type Server struct { 23 | Addr string 24 | 25 | KMS *keymanager.KeyManager 26 | DB db.DB 27 | 28 | // BaseIdentity optionally specifies a function that returns 29 | // the base context for incoming requests on this server. 30 | // The provided Listener is the specific Listener that's 31 | // about to start accepting requests. 32 | // If BaseContext is nil, the default is keri.New(). 33 | // If non-nil, it must return a non-nil context. 34 | BaseIdentity func(net.Listener) *keri.Keri 35 | 36 | // ConnIdentity optionally specifies a function that modifies 37 | // the context used for a new connection c. The provided ctx 38 | // is derived from the base context and has a ServerContextKey 39 | // value. 40 | ConnIdentity func(base *keri.Keri, prefix string, c net.Conn) *keri.Keri 41 | 42 | connLock sync.Mutex 43 | conns []*conn 44 | } 45 | 46 | func (r *Server) ListenAndServe() error { 47 | 48 | if r.Addr == "" { 49 | r.Addr = DefaultDirectModeAddr 50 | } 51 | 52 | ln, err := net.Listen("tcp", r.Addr) 53 | if err != nil { 54 | return errors.Wrapf(err, "unable to listen on %s", r.Addr) 55 | } 56 | 57 | return r.Serve(ln) 58 | } 59 | 60 | func (r *Server) Serve(l net.Listener) error { 61 | 62 | if r.KMS == nil { 63 | r.KMS = defaultKMS() 64 | } 65 | 66 | if r.DB == nil { 67 | r.DB = mem.New() 68 | } 69 | 70 | baseID, err := keri.New(r.KMS, r.DB) 71 | if err != nil { 72 | return err 73 | } 74 | 75 | if r.BaseIdentity != nil { 76 | baseID = r.BaseIdentity(l) 77 | } 78 | 79 | for { 80 | c, err := l.Accept() 81 | if err != nil { 82 | return errors.Wrap(err, "unexpected error accepting connection") 83 | } 84 | 85 | br := stream.NewReader(c) 86 | 87 | firstMsg, err := br.Read() 88 | if err != nil { 89 | log.Println("error reading initial message on connection", err) 90 | c.Close() 91 | continue 92 | } 93 | 94 | ioc := &conn{ 95 | reader: br, 96 | conn: c, 97 | writer: stream.NewWriter(c), 98 | } 99 | 100 | r.addConnection(ioc) 101 | 102 | connID := baseID 103 | pre := firstMsg.Event.Prefix 104 | 105 | if cc := r.ConnIdentity; cc != nil { 106 | connID = cc(connID, pre, c) 107 | if connID == nil { 108 | panic("ConnIdentity returned nil") 109 | } 110 | } 111 | 112 | if firstMsg.Event.ILK() == event.ICP || firstMsg.Event.ILK() == event.DIP { 113 | _, err := connID.FindConnection(pre) 114 | if err != nil { 115 | err = sendOwnInception(connID, ioc) 116 | if err != nil { 117 | log.Println("error sending own icp to connection", err) 118 | c.Close() 119 | continue 120 | } 121 | } 122 | } 123 | 124 | outmsgs, err := connID.ProcessEvents(firstMsg) 125 | if err != nil { 126 | log.Println("error reading initial message on connection", err) 127 | c.Close() 128 | continue 129 | } 130 | 131 | for _, msg := range outmsgs { 132 | err := ioc.Write(msg) 133 | if err != nil { 134 | log.Println("error writing initial message to connection", err) 135 | c.Close() 136 | continue 137 | } 138 | } 139 | 140 | go func() { 141 | err := handleConnection(ioc, connID) 142 | r.removeConnection(ioc) 143 | log.Printf("server connection closed with : (%v)\n", err) 144 | }() 145 | } 146 | } 147 | 148 | func (r *Server) addConnection(conn *conn) { 149 | r.connLock.Lock() 150 | defer r.connLock.Unlock() 151 | 152 | r.conns = append(r.conns, conn) 153 | } 154 | 155 | func (r *Server) removeConnection(conn *conn) { 156 | r.connLock.Lock() 157 | defer r.connLock.Unlock() 158 | 159 | idx := -1 160 | for i, c := range r.conns { 161 | if c == conn { 162 | idx = i 163 | break 164 | } 165 | } 166 | 167 | if idx != -1 { 168 | r.conns = append(r.conns[:idx], r.conns[idx+1:]...) 169 | } 170 | } 171 | 172 | func defaultKMS() *keymanager.KeyManager { 173 | kms, _ := keymanager.NewKeyManager(keymanager.WithStore(mem.New())) 174 | return kms 175 | } 176 | 177 | func sendOwnInception(id *keri.Keri, ioc *conn) error { 178 | icp, _ := id.Inception() 179 | 180 | err := ioc.Write(icp) 181 | if err != nil { 182 | return errors.Wrap(err, "unable to write inception event") 183 | } 184 | 185 | return nil 186 | 187 | } 188 | -------------------------------------------------------------------------------- /pkg/event/receipt.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "bytes" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/pkg/errors" 9 | 10 | "github.com/decentralized-identity/kerigo/pkg/derivation" 11 | "github.com/decentralized-identity/kerigo/pkg/version" 12 | ) 13 | 14 | type Receipt struct { 15 | Prefix string 16 | Digest string 17 | Sequence int 18 | 19 | RctType ILK 20 | Signature *derivation.Derivation //The Signature of the receipted event 21 | 22 | EstPrefix string // The Witness Identifier Prefix for Receipt Signatures (Transferable and Non-Transferable) 23 | EstSequence int // The sn of Latest Establishment Event for Transferable Receipt signatures 24 | EstDigest string // The dig of Latest Establishment Event for Transferable Receipt signatures 25 | 26 | txt []byte 27 | bin []byte 28 | } 29 | 30 | type ReceiptOpt func(r *Receipt) error 31 | 32 | func NewReceipt(evt *Event, opts ...ReceiptOpt) (*Receipt, error) { 33 | dig, err := evt.GetDigest() 34 | if err != nil { 35 | return nil, errors.Wrap(err, "unable to get digest to create event") 36 | } 37 | r := &Receipt{ 38 | RctType: RCT, 39 | Prefix: evt.Prefix, 40 | Digest: dig, 41 | Sequence: evt.SequenceInt(), 42 | } 43 | 44 | for _, opt := range opts { 45 | err := opt(r) 46 | if err != nil { 47 | return nil, err 48 | } 49 | } 50 | 51 | if r.RctType == RCT && (len(r.EstPrefix) > 0 && r.EstSequence >= 0 && r.Signature != nil) { 52 | return r, nil 53 | } 54 | 55 | if r.RctType == VRC && (len(r.EstPrefix) > 0 && r.EstSequence >= 0 && len(r.EstDigest) > 0 && r.Signature != nil) { 56 | return r, nil 57 | } 58 | 59 | return nil, errors.New("invalid receipt") 60 | } 61 | 62 | func WithEstablishmentEvent(est *Event) ReceiptOpt { 63 | return func(r *Receipt) error { 64 | s, err := SealEstablishment(est) 65 | if err != nil { 66 | return errors.Wrap(err, "error sealing establishment event for receipt") 67 | } 68 | 69 | r.RctType = VRC 70 | r.EstPrefix = s.Prefix 71 | r.EstSequence = s.SequenceInt() 72 | r.EstDigest = s.Digest 73 | 74 | return nil 75 | } 76 | } 77 | 78 | func WithEstablishmentSeal(s *Seal) ReceiptOpt { 79 | return func(r *Receipt) error { 80 | r.RctType = VRC 81 | r.EstPrefix = s.Prefix 82 | r.EstSequence = s.SequenceInt() 83 | r.EstDigest = s.Digest 84 | 85 | return nil 86 | } 87 | } 88 | 89 | func WithQB64(qb64 []byte) ReceiptOpt { 90 | return func(r *Receipt) error { 91 | r.txt = qb64 92 | 93 | return nil 94 | } 95 | } 96 | 97 | func WithSignature(der *derivation.Derivation) ReceiptOpt { 98 | return func(r *Receipt) error { 99 | r.Signature = der 100 | return nil 101 | } 102 | } 103 | 104 | func WithSignerPrefix(pre string) ReceiptOpt { 105 | return func(r *Receipt) error { 106 | r.EstPrefix = pre 107 | return nil 108 | } 109 | } 110 | 111 | func (r *Receipt) Text() []byte { 112 | if r.txt == nil { 113 | switch r.RctType { 114 | case VRC: 115 | o := derivation.NewOrdinal(uint16(r.EstSequence)) 116 | quadlet := strings.Join([]string{r.EstPrefix, string(o.Base64()), r.EstDigest, r.Signature.AsPrefix()}, "") 117 | r.txt = []byte(quadlet) 118 | case RCT: 119 | couplet := strings.Join([]string{r.EstPrefix, r.Signature.AsPrefix()}, "") 120 | r.txt = []byte(couplet) 121 | } 122 | } 123 | 124 | return r.txt 125 | } 126 | 127 | func (r *Receipt) Bin() []byte { 128 | if r.bin == nil { 129 | pre, _ := derivation.FromPrefix(r.EstPrefix) 130 | 131 | switch r.RctType { 132 | case VRC: 133 | dig, _ := derivation.FromPrefix(r.EstDigest) 134 | o := derivation.NewOrdinal(uint16(r.EstSequence)) 135 | quadlet := bytes.Join([][]byte{pre.Raw, o.Base64(), dig.Raw, r.Signature.Raw}, []byte{}) 136 | r.bin = quadlet 137 | case RCT: 138 | couplet := bytes.Join([][]byte{pre.Raw, r.Signature.Raw}, []byte{}) 139 | r.bin = couplet 140 | } 141 | } 142 | 143 | return r.bin 144 | } 145 | 146 | func (r *Receipt) Message() (*Message, error) { 147 | 148 | opts := []EventOption{ 149 | WithType(r.RctType), 150 | WithSequence(r.Sequence), 151 | WithPrefix(r.Prefix), 152 | WithDefaultVersion(JSON), 153 | } 154 | 155 | switch r.RctType { 156 | case VRC: 157 | s, _ := NewEventSeal(r.EstDigest, r.EstPrefix, strconv.Itoa(r.EstSequence)) 158 | opts = append(opts, WithSeals([]*Seal{s})) 159 | case RCT: 160 | s, _ := NewSeal(EventSeal, WithSealPrefix(r.EstPrefix)) 161 | opts = append(opts, WithSeals([]*Seal{s})) 162 | } 163 | 164 | receipt, err := NewEvent( 165 | opts..., 166 | ) 167 | 168 | if err != nil { 169 | return nil, errors.Wrap(err, "unable to create new event") 170 | } 171 | 172 | receipt.EventDigest = r.Digest 173 | 174 | eventBytes, err := Serialize(receipt, JSON) 175 | if err != nil { 176 | return nil, errors.Wrap(err, "unexpected error serializing receipt") 177 | } 178 | 179 | receipt.Version = VersionString(JSON, version.Code(), len(eventBytes)) 180 | 181 | msg := &Message{ 182 | Event: receipt, 183 | Signatures: []derivation.Derivation{*r.Signature}, 184 | } 185 | 186 | return msg, nil 187 | } 188 | -------------------------------------------------------------------------------- /pkg/direct/direct_test.go: -------------------------------------------------------------------------------- 1 | package direct 2 | 3 | import ( 4 | "net" 5 | "sync/atomic" 6 | "testing" 7 | "time" 8 | 9 | "github.com/stretchr/testify/assert" 10 | 11 | "github.com/decentralized-identity/kerigo/pkg/db/badger" 12 | "github.com/decentralized-identity/kerigo/pkg/db/mem" 13 | "github.com/decentralized-identity/kerigo/pkg/event" 14 | "github.com/decentralized-identity/kerigo/pkg/keri" 15 | "github.com/decentralized-identity/kerigo/pkg/test/kms" 16 | "github.com/decentralized-identity/kerigo/pkg/test/util" 17 | ) 18 | 19 | func TestDial(t *testing.T) { 20 | addr := ":5904" 21 | bobSecrets := []string{ 22 | "Alntkt3u6dDgiQxTATr01dy8M72uuaZEf9eTdM-70Gk8", 23 | "A1-QxDkso9-MR1A8rZz_Naw6fgaAtayda8hrbkRVVu1E", 24 | } 25 | 26 | bobKMS := kms.GetKMS(t, bobSecrets, mem.New()) 27 | 28 | bobID, err := keri.New(bobKMS, mem.New()) 29 | assert.NoError(t, err) 30 | 31 | cli, err := Dial(bobID, addr) 32 | assert.Error(t, err) 33 | assert.Nil(t, cli) 34 | 35 | } 36 | 37 | func TestSingleMessage(t *testing.T) { 38 | eveSecrets := []string{ 39 | "ArwXoACJgOleVZ2PY7kXn7rA0II0mHYDhc6WrBH8fDAc", 40 | "A6zz7M08-HQSFq92sJ8KJOT2cZ47x7pXFQLPB0pckB3Q", 41 | } 42 | addr := ":5901" 43 | 44 | eveTD, eveCancel := util.GetTempDir(t) 45 | defer eveCancel() 46 | 47 | eveDB, err := badger.New(eveTD) 48 | assert.NoError(t, err) 49 | 50 | eveKMS := kms.GetKMS(t, eveSecrets, eveDB) 51 | 52 | eveID, err := keri.New(eveKMS, eveDB) 53 | assert.NoError(t, err) 54 | 55 | srv := &Server{ 56 | Addr: addr, 57 | BaseIdentity: func(l net.Listener) *keri.Keri { 58 | return eveID 59 | }, 60 | } 61 | 62 | go func() { 63 | err = srv.ListenAndServe() 64 | }() 65 | 66 | bobSecrets := []string{ 67 | "Alntkt3u6dDgiQxTATr01dy8M72uuaZEf9eTdM-70Gk8", 68 | "A1-QxDkso9-MR1A8rZz_Naw6fgaAtayda8hrbkRVVu1E", 69 | "AKuYMe09COczwf2nIoD5AE119n7GLFOVFlNLxZcKuswc", 70 | } 71 | 72 | bobTD, bobCancel := util.GetTempDir(t) 73 | defer bobCancel() 74 | 75 | bobDB, err := badger.New(bobTD) 76 | assert.NoError(t, err) 77 | 78 | bobKMS := kms.GetKMS(t, bobSecrets, bobDB) 79 | 80 | bobID, err := keri.New(bobKMS, bobDB) 81 | assert.NoError(t, err) 82 | 83 | cli, err := DialTimeout(bobID, addr, 5*time.Second) 84 | 85 | msg, err := bobID.Inception() 86 | assert.NoError(t, err) 87 | 88 | err = cli.Write(msg) 89 | assert.NoError(t, err) 90 | 91 | assert.Eventually(t, func() bool { 92 | _, err := eveID.FindConnection("EQP28yaaIK9NBwG0Xr1kLqJCdsly7TCXEhX4yJdLnC3s") 93 | return err == nil 94 | }, 5*time.Second, 50*time.Millisecond) 95 | 96 | assert.Eventually(t, func() bool { 97 | _, err := bobID.FindConnection("EH7Oq9oxCgYa-nnNLvwhp9sFZpALILlRYyB-6n4WDi7w") 98 | return err == nil 99 | }, 5*time.Second, 50*time.Millisecond) 100 | 101 | assert.Equal(t, "EH7Oq9oxCgYa-nnNLvwhp9sFZpALILlRYyB-6n4WDi7w", eveID.Prefix()) 102 | assert.Equal(t, "EQP28yaaIK9NBwG0Xr1kLqJCdsly7TCXEhX4yJdLnC3s", bobID.Prefix()) 103 | 104 | err = cli.Close() 105 | assert.NoError(t, err) 106 | 107 | } 108 | 109 | func xTestWithNotify(t *testing.T) { 110 | eveSecrets := []string{ 111 | "ArwXoACJgOleVZ2PY7kXn7rA0II0mHYDhc6WrBH8fDAc", 112 | "A6zz7M08-HQSFq92sJ8KJOT2cZ47x7pXFQLPB0pckB3Q", 113 | } 114 | addr := ":5902" 115 | 116 | eveKMS := kms.GetKMS(t, eveSecrets, mem.New()) 117 | 118 | eveID, err := keri.New(eveKMS, mem.New()) 119 | assert.NoError(t, err) 120 | 121 | srv := &Server{ 122 | Addr: addr, 123 | BaseIdentity: func(l net.Listener) *keri.Keri { 124 | return eveID 125 | }, 126 | } 127 | 128 | go func() { 129 | err = srv.ListenAndServe() 130 | }() 131 | 132 | bobSecrets := []string{ 133 | "Alntkt3u6dDgiQxTATr01dy8M72uuaZEf9eTdM-70Gk8", 134 | "A1-QxDkso9-MR1A8rZz_Naw6fgaAtayda8hrbkRVVu1E", 135 | "AKuYMe09COczwf2nIoD5AE119n7GLFOVFlNLxZcKuswc", 136 | } 137 | 138 | bobKMS := kms.GetKMS(t, bobSecrets, mem.New()) 139 | 140 | bobID, err := keri.New(bobKMS, mem.New()) 141 | assert.NoError(t, err) 142 | 143 | cli, err := DialTimeout(bobID, addr, 5*time.Second) 144 | 145 | msg, err := bobID.Inception() 146 | assert.NoError(t, err) 147 | 148 | var rcptReceived atomic.Value 149 | rcptReceived.Store(false) 150 | err = cli.WriteNotify(msg, func(rcpt *event.Event, err error) { 151 | rcptReceived.Store(true) 152 | }) 153 | assert.NoError(t, err) 154 | 155 | assert.Eventually(t, func() bool { 156 | _, err := eveID.FindConnection("EQP28yaaIK9NBwG0Xr1kLqJCdsly7TCXEhX4yJdLnC3s") 157 | return err == nil 158 | }, 5*time.Second, 50*time.Millisecond) 159 | 160 | assert.Eventually(t, func() bool { 161 | _, err := bobID.FindConnection("EH7Oq9oxCgYa-nnNLvwhp9sFZpALILlRYyB-6n4WDi7w") 162 | return err == nil 163 | }, 5*time.Second, 50*time.Millisecond) 164 | 165 | assert.Eventually(t, func() bool { 166 | return rcptReceived.Load().(bool) 167 | }, 5*time.Second, 50*time.Millisecond) 168 | 169 | assert.Equal(t, "EH7Oq9oxCgYa-nnNLvwhp9sFZpALILlRYyB-6n4WDi7w", eveID.Prefix()) 170 | assert.Equal(t, "EQP28yaaIK9NBwG0Xr1kLqJCdsly7TCXEhX4yJdLnC3s", bobID.Prefix()) 171 | 172 | err = cli.Close() 173 | assert.NoError(t, err) 174 | 175 | } 176 | -------------------------------------------------------------------------------- /pkg/keymanager/keymanager_test.go: -------------------------------------------------------------------------------- 1 | package keymanager 2 | 3 | import ( 4 | "encoding/base64" 5 | "testing" 6 | 7 | "github.com/google/tink/go/aead" 8 | "github.com/google/tink/go/keyset" 9 | "github.com/stretchr/testify/assert" 10 | 11 | "github.com/decentralized-identity/kerigo/pkg/db/mem" 12 | ) 13 | 14 | var ( 15 | secrets = []string{ 16 | "AgjD4nRlycmM5cPcAkfOATAp8wVldRsnc9f1tiwctXlw", 17 | "AKUotEE0eAheKdDJh9QvNmSEmO_bjIav8V_GmctGpuCQ", 18 | "AK-nVhMMJciMPvmF5VZE_9H-nhrgng9aJWf7_UHPtRNM", 19 | "AT2cx-P5YUjIw_SLCHQ0pqoBWGk9s4N1brD-4pD_ANbs", 20 | "Ap5waegfnuP6ezC18w7jQiPyQwYYsp9Yv9rYMlKAYL8k", 21 | "Aqlc_FWWrxpxCo7R12uIz_Y2pHUH2prHx1kjghPa8jT8", 22 | "AagumsL8FeGES7tYcnr_5oN6qcwJzZfLKxoniKUpG4qc", 23 | "ADW3o9m3udwEf0aoOdZLLJdf1aylokP0lwwI_M2J9h0s", 24 | } 25 | ) 26 | 27 | func keyMgr(t *testing.T, opts ...Option) *KeyManager { 28 | kh, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) 29 | assert.NoError(t, err) 30 | 31 | a, err := aead.New(kh) 32 | assert.NoError(t, err) 33 | 34 | opts = append(opts, WithAEAD(a)) 35 | opts = append(opts, WithStore(mem.New())) 36 | km, err := NewKeyManager(opts...) 37 | assert.NoError(t, err) 38 | return km 39 | } 40 | 41 | func TestKeyManager(t *testing.T) { 42 | t.Run("new", func(t *testing.T) { 43 | km := keyMgr(t) 44 | assert.NotNil(t, km) 45 | 46 | pub := km.PublicKey() 47 | assert.NotEmpty(t, pub) 48 | }) 49 | 50 | t.Run("rotate", func(t *testing.T) { 51 | km := keyMgr(t) 52 | assert.NotNil(t, km) 53 | 54 | pub := km.PublicKey() 55 | assert.NotEmpty(t, pub) 56 | 57 | err := km.Rotate() 58 | assert.NoError(t, err) 59 | 60 | pub = km.PublicKey() 61 | assert.NotEmpty(t, pub) 62 | }) 63 | 64 | t.Run("sign", func(t *testing.T) { 65 | km := keyMgr(t) 66 | assert.NotNil(t, km) 67 | 68 | signer := km.Signer() 69 | b, err := signer([]byte("test data")) 70 | assert.NoError(t, err) 71 | assert.NotEmpty(t, b) 72 | }) 73 | 74 | t.Run("sign after rotate", func(t *testing.T) { 75 | km := keyMgr(t) 76 | assert.NotNil(t, km) 77 | 78 | err := km.Rotate() 79 | assert.NoError(t, err) 80 | 81 | signer := km.Signer() 82 | b, err := signer([]byte("test data")) 83 | assert.NoError(t, err) 84 | assert.NotEmpty(t, b) 85 | }) 86 | 87 | } 88 | 89 | func TestKeyManagerWithSecrets(t *testing.T) { 90 | 91 | t.Run("new", func(t *testing.T) { 92 | km := keyMgr(t, WithSecrets(secrets)) 93 | assert.NotNil(t, km) 94 | 95 | pub := km.PublicKey() 96 | enc := base64.URLEncoding.EncodeToString(pub) 97 | assert.Equal(t, "8KY1sKmgyjAiUDdUBPNPyrSz_ad_Qf9yzhDNZlEKiMc=", enc) 98 | }) 99 | 100 | t.Run("rotate", func(t *testing.T) { 101 | km := keyMgr(t, WithSecrets(secrets)) 102 | assert.NotNil(t, km) 103 | 104 | pub := km.PublicKey() 105 | enc := base64.URLEncoding.EncodeToString(pub) 106 | assert.Equal(t, "8KY1sKmgyjAiUDdUBPNPyrSz_ad_Qf9yzhDNZlEKiMc=", enc) 107 | 108 | err := km.Rotate() 109 | assert.NoError(t, err) 110 | 111 | pub = km.PublicKey() 112 | enc = base64.URLEncoding.EncodeToString(pub) 113 | assert.Equal(t, "bWeWTNGXPMQrVuJmScNQn81YF7T2fhh2kXwT8E_NbeI=", enc) 114 | 115 | }) 116 | 117 | t.Run("rotate past handles", func(t *testing.T) { 118 | km := keyMgr(t, WithSecrets(secrets)) 119 | assert.NotNil(t, km) 120 | 121 | pub := km.PublicKey() 122 | enc := base64.URLEncoding.EncodeToString(pub) 123 | assert.Equal(t, "8KY1sKmgyjAiUDdUBPNPyrSz_ad_Qf9yzhDNZlEKiMc=", enc) 124 | 125 | for i := 0; i < 6; i++ { 126 | err := km.Rotate() 127 | assert.NoError(t, err) 128 | } 129 | 130 | pub = km.PublicKey() 131 | enc = base64.URLEncoding.EncodeToString(pub) 132 | assert.Equal(t, "AIyL2yT9nU6kChGXWce8d6q07l0vBLPNImw_f9bazeQ=", enc) 133 | 134 | err := km.Rotate() 135 | assert.NoError(t, err) 136 | 137 | pub = km.PublicKey() 138 | assert.NotEmpty(t, pub) 139 | }) 140 | 141 | t.Run("sign", func(t *testing.T) { 142 | km := keyMgr(t, WithSecrets(secrets)) 143 | assert.NotNil(t, km) 144 | 145 | signer := km.Signer() 146 | b, err := signer([]byte("test data")) 147 | assert.NoError(t, err) 148 | 149 | enc := base64.URLEncoding.EncodeToString(b) 150 | assert.Equal(t, "oMG5Z5l5PgrUeIlY93X9mi-v10gqiR7Ojq6nnHHfiEE6QymzaK5pjyfglgvJo3Ve_F_aD_lRL-Dx9jpVes5fCw==", enc) 151 | }) 152 | 153 | t.Run("sign after rotate", func(t *testing.T) { 154 | km := keyMgr(t, WithSecrets(secrets)) 155 | assert.NotNil(t, km) 156 | 157 | err := km.Rotate() 158 | assert.NoError(t, err) 159 | 160 | signer := km.Signer() 161 | b, err := signer([]byte("test data")) 162 | assert.NoError(t, err) 163 | 164 | enc := base64.URLEncoding.EncodeToString(b) 165 | assert.Equal(t, "s0Y1hRUeLfysPULLmROLMkUpvFIRlfsepViLxaIJ5Tq9VA0j2feZ4y81-qn4xAs5WF_NUU5xtMOEsxf1dWpPBA==", enc) 166 | }) 167 | 168 | } 169 | 170 | func TestDB(t *testing.T) { 171 | t.Run("rotate", func(t *testing.T) { 172 | kh, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) 173 | assert.NoError(t, err) 174 | 175 | a, err := aead.New(kh) 176 | assert.NoError(t, err) 177 | 178 | db := mem.New() 179 | 180 | km1, err := NewKeyManager(WithAEAD(a), WithStore(db)) 181 | assert.NoError(t, err) 182 | 183 | assert.NotNil(t, km1) 184 | 185 | pub := km1.PublicKey() 186 | assert.NotEmpty(t, pub) 187 | 188 | err = km1.Rotate() 189 | assert.NoError(t, err) 190 | 191 | pub = km1.PublicKey() 192 | assert.NotEmpty(t, pub) 193 | 194 | sig := km1.Signer() 195 | enc, err := sig([]byte("test data")) 196 | assert.NoError(t, err) 197 | 198 | km2, err := NewKeyManager(WithAEAD(a), WithStore(db)) 199 | assert.NoError(t, err) 200 | 201 | sig2 := km2.Signer() 202 | enc2, err := sig2([]byte("test data")) 203 | assert.NoError(t, err) 204 | 205 | assert.Equal(t, enc, enc2) 206 | 207 | }) 208 | } 209 | -------------------------------------------------------------------------------- /pkg/keymanager/keymanager.go: -------------------------------------------------------------------------------- 1 | package keymanager 2 | 3 | import ( 4 | "crypto/ed25519" 5 | "crypto/rand" 6 | "encoding/json" 7 | 8 | "github.com/google/tink/go/aead" 9 | "github.com/google/tink/go/signature/subtle" 10 | "github.com/google/tink/go/tink" 11 | "github.com/pkg/errors" 12 | 13 | "github.com/decentralized-identity/kerigo/pkg/db" 14 | "github.com/decentralized-identity/kerigo/pkg/derivation" 15 | ) 16 | 17 | type key struct { 18 | Pub ed25519.PublicKey `json:"pub"` 19 | Priv ed25519.PrivateKey 20 | PrivDer *derivation.Derivation 21 | signer *subtle.ED25519Signer 22 | } 23 | 24 | type KeyManager struct { 25 | secrets []string 26 | current *key 27 | next *key 28 | enveloper *aead.KMSEnvelopeAEAD 29 | store db.DB 30 | kw tink.AEAD 31 | } 32 | 33 | type Option func(*KeyManager) error 34 | 35 | func NewKeyManager(opts ...Option) (*KeyManager, error) { 36 | 37 | km := &KeyManager{ 38 | secrets: []string{}, 39 | kw: &dummyAEAD{}, 40 | } 41 | 42 | for _, o := range opts { 43 | err := o(km) 44 | if err != nil { 45 | return nil, err 46 | } 47 | } 48 | 49 | if km.store == nil { 50 | return nil, errors.New("must provide db") 51 | } 52 | 53 | km.enveloper = aead.NewKMSEnvelopeAEAD2(aead.AES256GCMKeyTemplate(), km.kw) 54 | 55 | err := km.loadKeys() 56 | if err != nil { 57 | km.current, err = km.nextKeys() 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | km.next, err = km.nextKeys() 63 | if err != nil { 64 | return nil, err 65 | } 66 | 67 | err := km.saveKeys() 68 | if err != nil { 69 | return nil, err 70 | } 71 | } 72 | 73 | return km, nil 74 | } 75 | 76 | type dummyAEAD struct{} 77 | 78 | func (d *dummyAEAD) Encrypt(plaintext, additionalData []byte) ([]byte, error) { 79 | return plaintext, nil 80 | } 81 | 82 | func (d *dummyAEAD) Decrypt(ciphertext, additionalData []byte) ([]byte, error) { 83 | return ciphertext, nil 84 | } 85 | 86 | func (r *KeyManager) nextKeys() (*key, error) { 87 | 88 | var privkey ed25519.PrivateKey 89 | if len(r.secrets) > 0 { 90 | var cur string 91 | cur, r.secrets = r.secrets[0], r.secrets[1:] 92 | 93 | der, err := derivation.FromPrefix(cur) 94 | if err != nil { 95 | return nil, err 96 | } 97 | privkey = ed25519.NewKeyFromSeed(der.Raw) 98 | 99 | } else { 100 | var err error 101 | _, privkey, err = ed25519.GenerateKey(rand.Reader) 102 | if err != nil { 103 | return nil, err 104 | } 105 | } 106 | pubkey := privkey.Public() 107 | 108 | signer, err := subtle.NewED25519SignerFromPrivateKey(&privkey) 109 | if err != nil { 110 | return nil, err 111 | } 112 | 113 | nextDer, err := derivation.New(derivation.WithCode(derivation.Ed25519), derivation.WithRaw(pubkey.(ed25519.PublicKey))) 114 | if err != nil { 115 | return nil, err 116 | } 117 | 118 | return &key{ 119 | Pub: pubkey.(ed25519.PublicKey), 120 | Priv: privkey, 121 | PrivDer: nextDer, 122 | signer: signer, 123 | }, nil 124 | 125 | } 126 | 127 | func (r *KeyManager) Signer() derivation.Signer { 128 | return r.current.signer.Sign 129 | } 130 | 131 | func (r *KeyManager) PublicKey() ed25519.PublicKey { 132 | return r.current.Pub 133 | } 134 | 135 | func (r *KeyManager) Next() *derivation.Derivation { 136 | return r.next.PrivDer 137 | } 138 | 139 | func (r *KeyManager) Rotate() error { 140 | next, err := r.nextKeys() 141 | if err != nil { 142 | return err 143 | } 144 | 145 | r.current = r.next 146 | r.next = next 147 | return r.saveKeys() 148 | } 149 | 150 | func (r *KeyManager) saveKeys() error { 151 | err := r.saveKey("current", r.current) 152 | if err != nil { 153 | return err 154 | } 155 | 156 | err = r.saveKey("next", r.next) 157 | if err != nil { 158 | return err 159 | } 160 | 161 | return nil 162 | } 163 | 164 | func (r *KeyManager) saveKey(name string, k *key) error { 165 | ser, err := json.Marshal(k) 166 | if err != nil { 167 | return errors.Wrap(err, "unexpected error marshalling next key") 168 | } 169 | 170 | enc, err := r.enveloper.Encrypt(ser, []byte{}) 171 | if err != nil { 172 | return errors.Wrap(err, "unexpect error trying to encrypt current key") 173 | } 174 | 175 | err = r.store.Put(name, enc) 176 | if err != nil { 177 | return errors.Wrapf(err, "unable to save key %s", name) 178 | } 179 | 180 | return nil 181 | } 182 | 183 | func (r *KeyManager) loadKeys() error { 184 | var err error 185 | r.current, err = r.loadKey("current") 186 | if err != nil { 187 | return err 188 | } 189 | 190 | r.next, err = r.loadKey("next") 191 | if err != nil { 192 | return err 193 | } 194 | 195 | return nil 196 | } 197 | 198 | func (r *KeyManager) loadKey(name string) (*key, error) { 199 | enc, err := r.store.Get(name) 200 | if err != nil { 201 | return nil, errors.Wrapf(err, "unable to load key %s", name) 202 | } 203 | 204 | ser, err := r.enveloper.Decrypt(enc, []byte{}) 205 | if err != nil { 206 | return nil, errors.Wrapf(err, "unable to load key %s", name) 207 | } 208 | 209 | k := &key{} 210 | err = json.Unmarshal(ser, k) 211 | if err != nil { 212 | return nil, errors.Wrapf(err, "unexpected error unmarshalling key %s", name) 213 | } 214 | 215 | k.signer, err = subtle.NewED25519SignerFromPrivateKey(&k.Priv) 216 | if err != nil { 217 | return nil, errors.Wrapf(err, "unexpected error creating signer for key %s", name) 218 | } 219 | 220 | return k, nil 221 | } 222 | 223 | func WithSecrets(s []string) Option { 224 | return func(km *KeyManager) error { 225 | km.secrets = s 226 | return nil 227 | } 228 | } 229 | 230 | func WithAEAD(a tink.AEAD) Option { 231 | return func(km *KeyManager) error { 232 | km.kw = a 233 | return nil 234 | } 235 | } 236 | 237 | func WithStore(store db.DB) Option { 238 | return func(km *KeyManager) error { 239 | km.store = store 240 | return nil 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /pkg/derivation/code.go: -------------------------------------------------------------------------------- 1 | package derivation 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | type Code int 8 | 9 | // Code constants represent the available hashing and encryptions algorithms for 10 | // the derivations. 11 | const ( 12 | Ed25519Seed Code = iota 13 | Ed25519NT 14 | X25519 15 | Ed25519 16 | Blake3256 17 | Blake2b256 18 | Blake2s256 19 | SHA3256 20 | SHA2256 21 | RandomSeed128 22 | Ed25519Sig 23 | EcDSASig 24 | Blake3512 25 | SHA3512 26 | Blake2b512 27 | SHA2512 28 | Ed25519Attached 29 | EcDSAAttached 30 | ) 31 | 32 | var ( 33 | codeValue = map[string]Code{ 34 | "A": Ed25519Seed, 35 | "B": Ed25519NT, 36 | "C": X25519, 37 | "D": Ed25519, 38 | "E": Blake3256, 39 | "F": Blake2b256, 40 | "G": Blake2s256, 41 | "H": SHA3256, 42 | "I": SHA2256, 43 | "0A": RandomSeed128, 44 | "0B": Ed25519Sig, 45 | "0C": EcDSASig, 46 | "0D": Blake3512, 47 | "0E": SHA3512, 48 | "0F": Blake2b512, 49 | "0G": SHA2512, 50 | "AX": Ed25519Attached, 51 | "BX": EcDSAAttached, 52 | } 53 | 54 | codeString = map[Code]string{ 55 | Ed25519Seed: "A", 56 | Ed25519NT: "B", 57 | X25519: "C", 58 | Ed25519: "D", 59 | Blake3256: "E", 60 | Blake2b256: "F", 61 | Blake2s256: "G", 62 | SHA3256: "H", 63 | SHA2256: "I", 64 | RandomSeed128: "0A", 65 | Ed25519Sig: "0B", 66 | EcDSASig: "0C", 67 | Blake3512: "0D", 68 | SHA3512: "0E", 69 | Blake2b512: "0F", 70 | SHA2512: "0G", 71 | Ed25519Attached: "AX", 72 | EcDSAAttached: "BX", 73 | } 74 | 75 | codeName = map[Code]string{ 76 | Ed25519Seed: "Ed25519Seed", 77 | Ed25519NT: "Ed25519NT", 78 | X25519: "X25519", 79 | Ed25519: "Ed25519", 80 | Blake3256: "Blake3256", 81 | Blake2b256: "Blake2b256", 82 | Blake2s256: "Blake2s256", 83 | SHA3256: "SHA3256", 84 | SHA2256: "SHA2256", 85 | RandomSeed128: "RandomSeed128", 86 | Ed25519Sig: "Ed25519Sig", 87 | EcDSASig: "EcDSASig", 88 | Blake3512: "Blake3512", 89 | SHA3512: "SHA3512", 90 | Blake2b512: "Blake2b512", 91 | SHA2512: "SHA2512", 92 | Ed25519Attached: "Ed25519Attached", 93 | EcDSAAttached: "EcDSAAttached", 94 | } 95 | 96 | codeDataLength = map[Code]int{ 97 | Ed25519Seed: 32, 98 | Ed25519NT: 32, 99 | X25519: 32, 100 | Ed25519: 32, 101 | Blake3256: 32, 102 | Blake2b256: 32, 103 | Blake2s256: 32, 104 | SHA3256: 32, 105 | SHA2256: 32, 106 | RandomSeed128: 16, 107 | Ed25519Sig: 64, 108 | EcDSASig: 64, 109 | Blake3512: 64, 110 | SHA3512: 64, 111 | Blake2b512: 64, 112 | SHA2512: 64, 113 | Ed25519Attached: 64, 114 | EcDSAAttached: 64, 115 | } 116 | 117 | codePrefixBase64Length = map[Code]int{ 118 | Ed25519Seed: 44, 119 | Ed25519NT: 44, 120 | X25519: 44, 121 | Ed25519: 44, 122 | Blake3256: 44, 123 | Blake2b256: 44, 124 | Blake2s256: 44, 125 | SHA3256: 44, 126 | SHA2256: 44, 127 | RandomSeed128: 24, 128 | Ed25519Sig: 88, 129 | EcDSASig: 88, 130 | Blake3512: 88, 131 | SHA3512: 88, 132 | Blake2b512: 88, 133 | SHA2512: 88, 134 | Ed25519Attached: 88, 135 | EcDSAAttached: 88, 136 | } 137 | 138 | codePrefixDataLength = map[Code]int{ 139 | Ed25519Seed: 33, 140 | Ed25519NT: 33, 141 | X25519: 33, 142 | Ed25519: 33, 143 | Blake3256: 33, 144 | Blake2b256: 33, 145 | Blake2s256: 33, 146 | SHA3256: 33, 147 | SHA2256: 33, 148 | RandomSeed128: 18, 149 | Ed25519Sig: 66, 150 | EcDSASig: 66, 151 | Blake3512: 66, 152 | SHA3512: 66, 153 | Blake2b512: 66, 154 | SHA2512: 66, 155 | Ed25519Attached: 66, 156 | EcDSAAttached: 66, 157 | } 158 | ) 159 | 160 | // String returns the 1, 2 or 4 character representation of the encoding. 161 | // This code string should be pre-pended to a base64 representation of 162 | // the derived data to make it self-describing 163 | func (c Code) String() string { 164 | return codeString[c] 165 | } 166 | 167 | // Length of the code representing the derivation (1, 2 or 4) 168 | func (c Code) Length() int { 169 | return len(c.String()) 170 | } 171 | 172 | // DataLength of the derived data 173 | func (c Code) DataLength() int { 174 | return codeDataLength[c] 175 | } 176 | 177 | // PrefixBase64Lenghth of the derived data after it has been 178 | // bsae64 encoded and the appropriate code has be prepended 179 | func (c Code) PrefixBase64Length() int { 180 | return codePrefixBase64Length[c] 181 | } 182 | 183 | // PrefixDataLength of the data with the code prepended 184 | func (c Code) PrefixDataLength() int { 185 | return codePrefixDataLength[c] 186 | } 187 | 188 | // Human readable name of the underlying algorithim 189 | // used in the derivation 190 | func (c Code) Name() string { 191 | return codeName[c] 192 | } 193 | 194 | // Default derivation data: used for calculating total data length 195 | // in some KERI functions 196 | func (c Code) Default() string { 197 | return strings.Repeat("#", c.PrefixBase64Length()) 198 | } 199 | 200 | // SelfAdressing derivaitons 201 | func (c Code) SelfAddressing() bool { 202 | switch c { 203 | case Blake3256, Blake3512, Blake2b256, Blake2b512, Blake2s256, SHA2256, SHA2512, SHA3256, SHA3512: 204 | return true 205 | } 206 | return false 207 | } 208 | 209 | // SelfSigning derivaitons 210 | func (c Code) SelfSigning() bool { 211 | switch c { 212 | case Ed25519Sig: 213 | return true 214 | } 215 | return false 216 | } 217 | 218 | // Basic derivations 219 | func (c Code) Basic() bool { 220 | switch c { 221 | case Ed25519NT, Ed25519: 222 | return true 223 | } 224 | return false 225 | } 226 | 227 | // AttachedSignature derivation 228 | func (c Code) AttachedSignature() bool { 229 | switch c { 230 | case Ed25519Attached, EcDSAAttached: 231 | return true 232 | } 233 | return false 234 | } 235 | -------------------------------------------------------------------------------- /pkg/event/event_test.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "bytes" 5 | "crypto/ed25519" 6 | "fmt" 7 | "math/big" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | 12 | "github.com/decentralized-identity/kerigo/pkg/derivation" 13 | "github.com/decentralized-identity/kerigo/pkg/prefix" 14 | ) 15 | 16 | func TestFormatFromVersion(t *testing.T) { 17 | assert := assert.New(t) 18 | 19 | f, err := FormatFromVersion(VersionString(JSON, "10", 123)) 20 | assert.Nil(err) 21 | assert.Equal(JSON, f) 22 | 23 | f, err = FormatFromVersion("KERI10JSON") 24 | assert.Nil(err) 25 | assert.Equal(JSON, f) 26 | 27 | f, err = FormatFromVersion(VersionString(CBOR, "10", 123)) 28 | assert.Nil(err) 29 | assert.Equal(CBOR, f) 30 | 31 | f, err = FormatFromVersion("KERI10CBOR") 32 | assert.Nil(err) 33 | assert.Equal(CBOR, f) 34 | 35 | f, err = FormatFromVersion(VersionString(MSGPK, "10", 123)) 36 | assert.Nil(err) 37 | assert.Equal(MSGPK, f) 38 | 39 | f, err = FormatFromVersion("KERI10MSGPK") 40 | assert.Nil(err) 41 | assert.Equal(MSGPK, f) 42 | 43 | f, err = FormatFromVersion("KERI10PROTO") 44 | assert.NotNil(err) 45 | assert.Equal(FORMAT(-1), f) 46 | } 47 | 48 | func TestVersionString(t *testing.T) { 49 | assert := assert.New(t) 50 | 51 | vs := VersionString(JSON, "10", 123) 52 | assert.Equal("KERI10JSON00007b_", vs) 53 | 54 | vs = VersionString(CBOR, "10", 123) 55 | assert.Equal("KERI10CBOR00007b_", vs) 56 | 57 | vs = VersionString(MSGPK, "10", 123) 58 | assert.Equal("KERI10MSGPK00007b_", vs) 59 | } 60 | 61 | func TestSerialize(t *testing.T) { 62 | assert := assert.New(t) 63 | 64 | //JSON 65 | expected := []byte(`{"v":"KERI10JSON0000fb_","i":"ETT9n-TCGn8XfkGkcNeNmZgdZSwHPLyDsojFXotBXdSo","s":"0","t":"icp","kt":"1","k":["DSuhyBcPZEZLK-fcw5tzHn2N46wRCG_ZOoeKtWTOunRA"],"n":"EGAPkzNZMtX-QiVgbRbyAIZGoXvbGv9IPb0foWTZvI_4","wt":"0","w":[],"c":[]}`) 66 | 67 | e := &Event{ 68 | Version: "KERI10JSON0000fb_", 69 | Prefix: "ETT9n-TCGn8XfkGkcNeNmZgdZSwHPLyDsojFXotBXdSo", 70 | EventType: "icp", 71 | Sequence: "0", 72 | SigThreshold: &SigThreshold{conditions: [][]*big.Rat{{big.NewRat(1, 1)}}}, 73 | WitnessThreshold: "0", 74 | Keys: []string{"DSuhyBcPZEZLK-fcw5tzHn2N46wRCG_ZOoeKtWTOunRA"}, 75 | Next: "EGAPkzNZMtX-QiVgbRbyAIZGoXvbGv9IPb0foWTZvI_4", 76 | Config: []prefix.Trait{}, 77 | Witnesses: []string{}, 78 | } 79 | 80 | jsonSer, err := Serialize(e, JSON) 81 | assert.Nil(err) 82 | assert.Equal(expected, jsonSer) 83 | 84 | jsonSer, err = e.Serialize() 85 | assert.Nil(err) 86 | assert.Equal(expected, jsonSer) 87 | 88 | } 89 | 90 | func TestDigest(t *testing.T) { 91 | assert := assert.New(t) 92 | 93 | //JSON 94 | data := []byte(`{"v":"KERI10JSON0000fb_","i":"ETT9n-TCGn8XfkGkcNeNmZgdZSwHPLyDsojFXotBXdSo","s":"0","t":"icp","kt":"1","k":["DSuhyBcPZEZLK-fcw5tzHn2N46wRCG_ZOoeKtWTOunRA"],"n":"EGAPkzNZMtX-QiVgbRbyAIZGoXvbGv9IPb0foWTZvI_4","wt":"0","w":[],"c":[]}`) 95 | expectedString := "ErgY910xsF3NSLGH4Yl6O9oEkdxj0FOujnHTD8W5V_AI" 96 | expectedBytes := []byte{174, 6, 61, 215, 76, 108, 23, 115, 82, 44, 97, 248, 98, 94, 142, 246, 129, 36, 119, 24, 244, 20, 235, 163, 156, 116, 195, 241, 110, 85, 252, 2} 97 | 98 | digestBytes, err := Digest(data, derivation.Blake3256) 99 | assert.Nil(err) 100 | assert.Equal(expectedBytes, digestBytes) 101 | 102 | digestString, err := DigestString(data, derivation.Blake3256) 103 | assert.Nil(err) 104 | assert.Equal(expectedString, digestString) 105 | } 106 | 107 | func TestSequenceInt(t *testing.T) { 108 | assert := assert.New(t) 109 | 110 | e := Event{Sequence: "0"} 111 | assert.Equal(0, e.SequenceInt()) 112 | 113 | e.Sequence = fmt.Sprintf("%x", 93840482) 114 | assert.Equal(93840482, e.SequenceInt()) 115 | } 116 | 117 | func TestNextDigest(t *testing.T) { 118 | assert := assert.New(t) 119 | d1, _ := derivation.FromPrefix("BrHLayDN-mXKv62DAjFLX1_Y5yEUe0vA9YPe_ihiKYHE") 120 | d1p := prefix.New(d1) 121 | d2, _ := derivation.FromPrefix("BujP_71bmWFVcvFmkE9uS8BTZ54GIstZ20nj_UloF8Rk") 122 | d2p := prefix.New(d2) 123 | d3, _ := derivation.FromPrefix("B8T4xkb8En6o0Uo5ZImco1_08gT5zcYnXzizUPVNzicw") 124 | d3p := prefix.New(d3) 125 | 126 | evnt, _ := NewEvent(WithType(ROT), WithKeys(d1p, d2p, d3p), WithThreshold(2), WithSequence(2)) 127 | 128 | next, err := NextDigest("2", derivation.Blake3256, d1p, d2p, d3p) 129 | assert.Nil(err) 130 | assert.Equal("ED8YvDrXvGuaIVZ69XsBVA5YN2pNTfQOFwgeloVHeWKs", next) 131 | 132 | next, err = evnt.NextDigest(derivation.Blake3256) 133 | assert.Nil(err) 134 | assert.Equal("ED8YvDrXvGuaIVZ69XsBVA5YN2pNTfQOFwgeloVHeWKs", next) 135 | 136 | next, err = NextDigest("1/2,1/2&1&1/4,1/4,1/4,1/4", derivation.Blake3256, d1p, d2p, d3p) 137 | assert.Nil(err) 138 | assert.Equal("EO5zVmvz-0yt1PlNvIG0iI-8X6qmkGwt-sQfcQ1GvmRc", next) 139 | 140 | evnt.SigThreshold, _ = NewMultiWeighted([]string{"1/2", "1/2"}, []string{"1"}, []string{"1/4", "1/4", "1/4", "1/4"}) 141 | next, err = evnt.NextDigest(derivation.Blake3256) 142 | assert.Nil(err) 143 | assert.Equal("EO5zVmvz-0yt1PlNvIG0iI-8X6qmkGwt-sQfcQ1GvmRc", next) 144 | 145 | //test case from Bob demo in python 146 | der, err := derivation.FromPrefix("A6zz7M08-HQSFq92sJ8KJOT2cZ47x7pXFQLPB0pckB3Q") 147 | assert.NoError(err) 148 | edPriv := ed25519.NewKeyFromSeed(der.Raw) 149 | edPub := edPriv.Public() 150 | 151 | basicDerivation, err := derivation.New(derivation.WithCode(derivation.Ed25519), derivation.WithRaw(edPub.(ed25519.PublicKey))) 152 | assert.Nil(err) 153 | basicPre := prefix.New(basicDerivation) 154 | 155 | next, err = NextDigest("1", derivation.Blake3256, basicPre) 156 | assert.NoError(err) 157 | assert.Equal("EPYuj8mq_PYYsoBKkzX1kxSPGYBWaIya3slgCOyOtlqU", next) 158 | 159 | } 160 | 161 | func TestReceiptMarshalling(t *testing.T) { 162 | quadlet := []byte(`EZNHWKpQpuUk5NCpMLPDlvsMAjQelNR1defp5wx30jtY0AAAAAAAAAAAAAAAAAAAAABAETVS3U7GGytIWRnh2gEpQSLLdgHn-FdehvTrlN0OunioAAnlC9GCUDY9jySn6zaO-iKSeeWfF5UHjEzCE819Sph6UGdCySNGbPjCYhGB4U5XM4K1Gm-1wNsTKpwSWyyr_MDg`) 163 | 164 | evt, err := ParseAttachedQuadlet(bytes.NewReader(quadlet)) 165 | assert.NoError(t, err) 166 | 167 | assert.Equal(t, evt.Sequence, 4) 168 | } 169 | -------------------------------------------------------------------------------- /pkg/derivation/attached-signature.go: -------------------------------------------------------------------------------- 1 | package derivation 2 | 3 | import ( 4 | "crypto/ed25519" 5 | "encoding/base64" 6 | "encoding/binary" 7 | "errors" 8 | "fmt" 9 | "io" 10 | ) 11 | 12 | // Attahced Signature derivations must provide a signer function 13 | func attachedSignatureDeriver(c Code) (d deriver) { 14 | return func(data []byte) ([]byte, error) { 15 | return nil, errors.New("For attached signature derivations must provide Signer function") 16 | } 17 | } 18 | 19 | // IndexToBase64 takes the provided index int and converts it to the correct 2 character base64 representation 20 | // Currently the index has to be less than 4095, which is the max encdoed value for a two character 21 | // base64 representation 22 | func IndexToBase64(index uint16) (string, error) { 23 | if index < 4096 { 24 | buf := make([]byte, 2) 25 | binary.BigEndian.PutUint16(buf, index) 26 | encoded := base64.RawURLEncoding.EncodeToString(append([]byte{0}, buf...))[1:] 27 | if index < 64 { 28 | return encoded[2:], nil 29 | } else { 30 | return encoded[1:], nil 31 | } 32 | } 33 | return "", errors.New("index must be less than 4095") 34 | } 35 | 36 | // Base64ToIndex converts a 2 character base64 index string into an int 37 | // Currently it only supports index strings up to 2 characters long 38 | func Base64ToIndex(index string) (uint16, error) { 39 | var convert string 40 | switch len(index) { 41 | case 1: 42 | convert = "AAA" + index 43 | case 2: 44 | convert = "AA" + index 45 | default: 46 | return 0, errors.New("index string can only be 2 characters long") 47 | } 48 | 49 | bytes, err := base64.RawURLEncoding.DecodeString(convert) 50 | if err != nil { 51 | return 0, err 52 | } 53 | 54 | return binary.BigEndian.Uint16(bytes[1:]), nil 55 | } 56 | 57 | // ParseSignatureCount takes a well formated 4 character signature derivation code 58 | // and returns the decoded count 59 | func ParseSignatureCount(count string) (uint16, error) { 60 | if len(count) != 4 { 61 | return 0, errors.New("signature count string must be 4 characters long") 62 | } 63 | 64 | if count[:1] != "-" || count[1:2] != "A" { 65 | return 0, errors.New("invalid count format. String must start with '-A'") 66 | } 67 | 68 | return Base64ToIndex(count[2:]) 69 | } 70 | 71 | // FromAttachedSignature parses an attached signature and returns 72 | // the appropriate type. These derivation codes are similar to 73 | // prefix derivation codes (i.e. they start with similar letters) 74 | // but are handled differently in the context of an attached signature 75 | // (namely they are two letter derivation codes but do not start with a "0" like 76 | // the prefix derivation codes do) 77 | func FromAttachedSignature(sig string) (*Derivation, error) { 78 | if len(sig) < 2 { 79 | return nil, errors.New("invalid signature string length") 80 | } 81 | var code Code 82 | switch sig[:1] { 83 | case "A": 84 | code = Ed25519Attached 85 | case "B": 86 | code = EcDSAAttached 87 | } 88 | 89 | if len(sig) != code.PrefixBase64Length() { 90 | return nil, errors.New("invalid signature string length") 91 | } 92 | 93 | der, err := New(WithCode(code)) 94 | if err != nil { 95 | return nil, fmt.Errorf("unable to parse attahced signature (%s)", err) 96 | } 97 | 98 | raw, err := base64.RawURLEncoding.DecodeString(sig[2:]) 99 | if err != nil { 100 | return nil, fmt.Errorf("unable to parse attahced signature (%s)", err) 101 | } 102 | 103 | if len(raw) != code.DataLength() { 104 | return nil, errors.New("invalid signature string length") 105 | } 106 | 107 | der.Raw = raw 108 | 109 | index, err := Base64ToIndex(sig[1:2]) 110 | if err != nil { 111 | return nil, fmt.Errorf("unable to parse signature key index (%s)", err) 112 | } 113 | 114 | der.KeyIndex = index 115 | 116 | return der, nil 117 | } 118 | 119 | // ParseAttachedSignatures takes an attached signatures string and parses 120 | // into individual derivations. This will return any unused bytes that remain 121 | // after parsing the the number of signatures indicated in the sig count. It 122 | // will error if there are not enough bytes for the number of signatures, or 123 | // if any individual signature is not sucessfully parsed. 124 | func ParseAttachedSignatures(buf io.Reader) ([]Derivation, error) { 125 | derivations := []Derivation{} 126 | 127 | sigCountBytes := make([]byte, 4) 128 | read, err := buf.Read(sigCountBytes) 129 | if read != 4 || err != nil { 130 | return nil, errors.New("invalid signature count") 131 | } 132 | 133 | sigCount, err := ParseSignatureCount(string(sigCountBytes)) 134 | if err != nil { 135 | return nil, fmt.Errorf("invalid signature count (%s)", err) 136 | } 137 | 138 | // iterate over the signatures bytes for each signature 139 | current := uint16(0) 140 | for current < sigCount { 141 | dCode := make([]byte, 1) 142 | read, err := buf.Read(dCode) 143 | if read != 1 || err != nil { 144 | return nil, fmt.Errorf("unable to read signature (%s)", err) 145 | } 146 | 147 | // get expected b64 length 148 | var sigString []byte 149 | if c, ok := codeValue[string(dCode)+"X"]; ok { 150 | sigString = make([]byte, c.PrefixBase64Length()-1) 151 | read, err := buf.Read(sigString) 152 | if read != c.PrefixBase64Length()-1 || err != nil { 153 | return nil, errors.New("invalid signature string length") 154 | } 155 | } else { 156 | return nil, fmt.Errorf("unable to determin signature derivation from code (%s)", string(dCode)) 157 | } 158 | 159 | der, err := FromAttachedSignature(string(append(dCode, sigString...))) 160 | if err != nil { 161 | return nil, err 162 | } 163 | 164 | derivations = append(derivations, *der) 165 | 166 | current++ 167 | } 168 | 169 | return derivations, nil 170 | } 171 | 172 | // VerifyWithAttachedSignature takes the key and signature derivations 173 | // and verifies the provided message bytes using the correct sig alg. 174 | func VerifyWithAttachedSignature(key, signature *Derivation, msg []byte) error { 175 | switch signature.Code { 176 | case Ed25519Attached: 177 | if !ed25519.Verify(key.Raw, msg, signature.Raw) { 178 | return errors.New("invalid message signature") 179 | } 180 | return nil 181 | } 182 | 183 | return errors.New("unknown or unsupported signature derivation") 184 | } 185 | -------------------------------------------------------------------------------- /pkg/event/sig-threshold.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "math/big" 7 | "strconv" 8 | "strings" 9 | 10 | "github.com/decentralized-identity/kerigo/pkg/derivation" 11 | "github.com/pkg/errors" 12 | ) 13 | 14 | type SigThreshold struct { 15 | conditions [][]*big.Rat 16 | } 17 | 18 | // MarshalJSON is used to correctly output the JSON since this field can 19 | // contain a single int, a list, or a list of lists 20 | func (s *SigThreshold) MarshalJSON() ([]byte, error) { 21 | switch len(s.conditions) { 22 | case 0: 23 | return json.Marshal("0") 24 | case 1: 25 | if len(s.conditions[0]) == 1 { 26 | return json.Marshal(s.conditions[0][0]) 27 | } 28 | return json.Marshal(s.conditions[0]) 29 | } 30 | 31 | return json.Marshal(s.conditions) 32 | } 33 | 34 | func (s *SigThreshold) UnmarshalJSON(in []byte) error { 35 | // check the smiplest first - that the input is a simple string 36 | if string(in[:1]) == `"` { 37 | parsed := "" 38 | err := json.Unmarshal(in, &parsed) 39 | if err != nil { 40 | return nil 41 | } 42 | 43 | tholdint, err := strconv.Atoi(parsed) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | s.conditions = [][]*big.Rat{{big.NewRat(int64(tholdint), 1)}} 49 | return nil 50 | } 51 | 52 | // it's either a list, or list of lists 53 | tholds := []interface{}{} 54 | 55 | err := json.Unmarshal(in, &tholds) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | if len(tholds) == 0 { 61 | s.conditions = [][]*big.Rat{} 62 | } else { 63 | // if the first item is a string, we will treat this as a weighted thresholed 64 | if _, ok := tholds[0].(string); ok { 65 | conditions := []string{} 66 | for _, t := range tholds { 67 | if ts, ok := t.(string); ok { 68 | conditions = append(conditions, ts) 69 | } 70 | } 71 | 72 | converted, err := parseConditions(conditions) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | s.conditions = append(s.conditions, converted) 78 | } 79 | 80 | // if the first item is a slice, we will treat this as a multi-weighted 81 | if _, ok := tholds[0].([]interface{}); ok { 82 | for _, t := range tholds { 83 | if ti, ok := t.([]interface{}); ok { 84 | conditions := []string{} 85 | for _, iface := range ti { 86 | if ts, ok := iface.(string); ok { 87 | conditions = append(conditions, ts) 88 | } 89 | } 90 | 91 | converted, err := parseConditions(conditions) 92 | if err != nil { 93 | return err 94 | } 95 | 96 | s.conditions = append(s.conditions, converted) 97 | } 98 | } 99 | } 100 | } 101 | 102 | return nil 103 | } 104 | 105 | // parseConditions iterates over string representation of fractional 106 | // weights and parses them to bit.Rat 107 | func parseConditions(conditions []string) ([]*big.Rat, error) { 108 | converted := []*big.Rat{} 109 | for _, c := range conditions { 110 | thold := new(big.Rat) 111 | 112 | _, err := fmt.Sscan(c, thold) 113 | if err != nil { 114 | return nil, fmt.Errorf("unable to parse condition %s: %s", c, err) 115 | } 116 | 117 | if thold.Sign() == -1 { 118 | return nil, errors.New("thresholds must be >= 0") 119 | } 120 | 121 | converted = append(converted, thold) 122 | } 123 | 124 | return converted, nil 125 | } 126 | 127 | // Satisfied takes the provided signature derivations and checkes each weighted 128 | // threshold 129 | func (s *SigThreshold) Satisfied(sigs []derivation.Derivation) bool { 130 | if !s.Weighted() { 131 | required := 0 132 | if len(s.conditions) == 1 && len(s.conditions[0]) == 1 { 133 | required = int(s.conditions[0][0].Num().Int64()) 134 | } 135 | return len(sigs) >= required 136 | } 137 | 138 | for _, c := range s.conditions { 139 | weight := big.NewRat(0, 1) 140 | for _, s := range sigs { 141 | // if our index is outside of the weighted list it doesn't count 142 | if int(s.KeyIndex) >= len(c) { 143 | continue 144 | } 145 | 146 | weight.Add(weight, c[int(s.KeyIndex)]) 147 | } 148 | 149 | // if we failed to meet this weight, return false 150 | if weight.Cmp(big.NewRat(1, 1)) == -1 { 151 | return false 152 | } 153 | } 154 | 155 | return true 156 | } 157 | 158 | // String returns the threshold as a raw string representation 159 | // sufficient for use in the next digest commitment 160 | func (s *SigThreshold) String() string { 161 | var b strings.Builder 162 | for i, conditions := range s.conditions { 163 | if i != 0 { 164 | b.Write([]byte(`&`)) 165 | } 166 | for j, c := range conditions { 167 | if j == 0 { 168 | fmt.Fprintf(&b, "%s", c.RatString()) 169 | } else { 170 | fmt.Fprintf(&b, ",%s", c.RatString()) 171 | } 172 | } 173 | 174 | } 175 | return b.String() 176 | } 177 | 178 | // returns true if this is a weighted threshold - i.e. there 179 | // is one or more lists of weights 180 | func (s *SigThreshold) Weighted() bool { 181 | if len(s.conditions) < 1 || (len(s.conditions) == 1 && len(s.conditions[0]) == 1) { 182 | return false 183 | } 184 | 185 | return true 186 | } 187 | 188 | // New returns a signing threshold requiring 'threshold' signatures 189 | func NewSigThreshold(threshold int64) (*SigThreshold, error) { 190 | if threshold < 0 { 191 | return nil, errors.New("threshold must be >= 0") 192 | } 193 | 194 | return &SigThreshold{[][]*big.Rat{{big.NewRat(threshold, 1)}}}, nil 195 | } 196 | 197 | // NewWeighted creates a new sighing threshold with a weighted multisig 198 | func NewWeighted(conditions ...string) (*SigThreshold, error) { 199 | sum := new(big.Rat) 200 | 201 | converted, err := parseConditions(conditions) 202 | if err != nil { 203 | return nil, err 204 | } 205 | 206 | for _, c := range converted { 207 | sum.Add(sum, c) 208 | } 209 | 210 | if sum.Cmp(big.NewRat(1, 1)) == -1 { 211 | return nil, errors.New("threshold not satifiable: sum of weights must be greather than 1") 212 | } 213 | 214 | return &SigThreshold{conditions: [][]*big.Rat{converted}}, nil 215 | } 216 | 217 | // NewMultiWeighted creates a new signing threshold with multiple 218 | // conditions 219 | func NewMultiWeighted(conditions ...[]string) (*SigThreshold, error) { 220 | thresholds := [][]*big.Rat{} 221 | 222 | for _, c := range conditions { 223 | tot := new(big.Rat) 224 | converted, err := parseConditions(c) 225 | if err != nil { 226 | return nil, err 227 | } 228 | 229 | for _, frac := range converted { 230 | tot = tot.Add(tot, frac) 231 | } 232 | 233 | if tot.Cmp(big.NewRat(1, 1)) == -1 { 234 | return nil, fmt.Errorf("threshold not statisfiable (%s)", c) 235 | } 236 | thresholds = append(thresholds, converted) 237 | } 238 | 239 | return &SigThreshold{conditions: thresholds}, nil 240 | } 241 | -------------------------------------------------------------------------------- /pkg/derivation/main.go: -------------------------------------------------------------------------------- 1 | // Package derivation provides the derivation logic for KERI 2 | // 3 | // Derivaitons are data blobs that have been hashed or encrypted using a 4 | // pre-defined set of algorithms. These data blobs are used to as the 5 | // foundation of a variety functionality (for example prefixes) 6 | // within KERI. Derivaitons are typically represented as Base64 encoded 7 | // strings with a specific 1, 2 or 4 character prefix, making them self describing. 8 | // This packages provides an accessible way to encode/decode the data bytes 9 | // to a given KERI defined derivaiton. 10 | package derivation 11 | 12 | import ( 13 | "encoding/base64" 14 | "fmt" 15 | "io" 16 | "strings" 17 | ) 18 | 19 | // DerivationOption is a genric configuration function for derivations 20 | type DerivationOption func(*Derivation) error 21 | 22 | // WithCode allows you to provide a derviation code for the derivation 23 | func WithCode(code Code) DerivationOption { 24 | return func(d *Derivation) error { 25 | d.Code = code 26 | if d.Code.Basic() { 27 | d.deriver = basicDeriver() 28 | } 29 | if d.Code.SelfAddressing() { 30 | d.deriver = selfAddressingDeriver(code) 31 | } 32 | if d.Code.SelfSigning() { 33 | d.deriver = selfSigningDeriver(code) 34 | } 35 | if d.Code.AttachedSignature() { 36 | d.deriver = attachedSignatureDeriver(code) 37 | } 38 | return nil 39 | } 40 | } 41 | 42 | // WithSigner uses the provided signing function to do the derivation 43 | func WithSigner(signer Signer) DerivationOption { 44 | return func(d *Derivation) error { 45 | d.deriver = deriver(signer) 46 | return nil 47 | } 48 | } 49 | 50 | // WithRaw allows you to provide raw derivation data 51 | func WithRaw(data []byte) DerivationOption { 52 | return func(d *Derivation) error { 53 | d.Raw = data 54 | return nil 55 | } 56 | } 57 | 58 | // deriver is the function responsible for actually processing the 59 | // raw data and returing the derived value 60 | type deriver func([]byte) ([]byte, error) 61 | 62 | // Derivation 63 | type Derivation struct { 64 | Code Code // The code for this derivation 65 | deriver deriver // return the derived data of the input 66 | Raw []byte // The Raw derived data 67 | KeyIndex uint16 // For Attached Signature Derivation - the index of the key for the signature 68 | } 69 | 70 | // Derive runs the derivation algorithm over the provided bytes 71 | // returning the derived data 72 | func (d *Derivation) Derive(data []byte) ([]byte, error) { 73 | raw, err := d.deriver(data) 74 | if err != nil { 75 | return nil, err 76 | } 77 | 78 | d.Raw = raw 79 | 80 | return raw, nil 81 | } 82 | 83 | // AsPrefix returns the derivation's raw data as a base 64 encoded string with 84 | // the correct derivation code prepended 85 | func (d *Derivation) AsPrefix() string { 86 | dcode := []byte(d.Code.String()) 87 | if d.Code.AttachedSignature() { 88 | indexBase64, _ := IndexToBase64(d.KeyIndex) 89 | dcode = append(dcode[:1], indexBase64...) 90 | } 91 | return string(append(dcode, base64.RawURLEncoding.EncodeToString(d.Raw)...)) 92 | } 93 | 94 | // New returns a derivation of the provided Code 95 | func New(options ...DerivationOption) (*Derivation, error) { 96 | d := &Derivation{} 97 | for _, option := range options { 98 | err := option(d) 99 | if err != nil { 100 | return nil, err 101 | } 102 | } 103 | return d, nil 104 | } 105 | 106 | // FromPrefix takes a prefix as input and returns the appropriate drivation 107 | // and raw (base64 unencoded) data represented by the prefix. 108 | func FromPrefix(data string) (*Derivation, error) { 109 | // Assumption: any valid prefix data will be over 4 bytes long, and if we know 110 | // that the data is at least that long, we can reference up to [:3] in the prefix 111 | // without going out of bounds 112 | if len(data) < 4 { 113 | return nil, fmt.Errorf("unable to determine derivation (%s)", "invalid prefix length") 114 | } 115 | 116 | var d *Derivation 117 | 118 | switch data[:1] { 119 | case "0": 120 | if dc, ok := codeValue[data[:2]]; ok { 121 | d, _ = New(WithCode(dc)) 122 | if len(data) != d.Code.PrefixBase64Length() { 123 | return nil, fmt.Errorf("invalid prefix length (%d) for derevation %s", len(data), d.Code.Name()) 124 | } 125 | } else { 126 | return nil, fmt.Errorf("unable to determin derevation from code %s", data[:1]) 127 | } 128 | default: 129 | // we are dealing with a single Letter prefix 130 | if dc, ok := codeValue[data[:1]]; ok { 131 | d, _ = New(WithCode(dc)) 132 | if len(data) != d.Code.PrefixBase64Length() { 133 | return nil, fmt.Errorf("invalid prefix length (%d) for derevation %s", len(data), d.Code.Name()) 134 | } 135 | } else { 136 | return nil, fmt.Errorf("unable to determin derevation from code %s", data[:1]) 137 | } 138 | } 139 | 140 | if d == nil { 141 | return nil, fmt.Errorf("unable to determin derevation") 142 | } 143 | 144 | raw, err := base64.RawURLEncoding.DecodeString(data[d.Code.Length():]) 145 | if err != nil || len(raw) != d.Code.DataLength() { 146 | return nil, fmt.Errorf("unable to parse prefix (%s)", err) 147 | } 148 | 149 | d.Raw = raw 150 | 151 | return d, nil 152 | } 153 | 154 | // ParsePrefix takes a prefix as input and returns the appropriate drivation 155 | // and raw (base64 unencoded) data represented by the prefix. 156 | func ParsePrefix(buf io.Reader) (*Derivation, error) { 157 | dCode := make([]byte, 1) 158 | read, err := buf.Read(dCode) 159 | if read != 1 || err != nil { 160 | return nil, fmt.Errorf("unable to read receipt (%s)", err) 161 | } 162 | 163 | var d *Derivation 164 | 165 | var code string 166 | switch dCode[0] { 167 | case '0': 168 | read, err = buf.Read(dCode) 169 | if read != 1 || err != nil { 170 | return nil, fmt.Errorf("unable to read receipt (%s)", err) 171 | } 172 | 173 | code = strings.Join([]string{"0", string(dCode[0])}, "") 174 | if dc, ok := codeValue[code]; ok { 175 | d, _ = New(WithCode(dc)) 176 | } else { 177 | return nil, fmt.Errorf("unable to determin derevation from code %s", dCode[:1]) 178 | } 179 | default: 180 | // we are dealing with a single Letter prefix 181 | code = string(dCode[0]) 182 | if dc, ok := codeValue[code]; ok { 183 | d, _ = New(WithCode(dc)) 184 | } else { 185 | return nil, fmt.Errorf("unable to determin derevation from code %s", dCode[:1]) 186 | } 187 | } 188 | 189 | if d == nil { 190 | return nil, fmt.Errorf("unable to determin derevation") 191 | } 192 | 193 | dlen := d.Code.PrefixBase64Length() - len(code) 194 | 195 | rest := make([]byte, dlen) 196 | read, err = buf.Read(rest) 197 | if read != dlen || err != nil { 198 | return nil, fmt.Errorf("unable to read receipt (%s)", err) 199 | } 200 | 201 | data := string(rest) 202 | 203 | raw, err := base64.RawURLEncoding.DecodeString(data) 204 | if err != nil || len(raw) != d.Code.DataLength() { 205 | return nil, fmt.Errorf("unable to parse prefix (%s)", err) 206 | } 207 | 208 | d.Raw = raw 209 | 210 | return d, nil 211 | } 212 | -------------------------------------------------------------------------------- /pkg/keri/keri_test.go: -------------------------------------------------------------------------------- 1 | package keri 2 | 3 | import ( 4 | "encoding/base64" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | 10 | "github.com/decentralized-identity/kerigo/pkg/db/mem" 11 | "github.com/decentralized-identity/kerigo/pkg/encoding/stream" 12 | "github.com/decentralized-identity/kerigo/pkg/event" 13 | testkms "github.com/decentralized-identity/kerigo/pkg/test/kms" 14 | ) 15 | 16 | func TestInception(t *testing.T) { 17 | secrets := []string{"ADW3o9m3udwEf0aoOdZLLJdf1aylokP0lwwI_M2J9h0s", "AagumsL8FeGES7tYcnr_5oN6qcwJzZfLKxoniKUpG4qc"} 18 | kms := testkms.GetKMS(t, secrets, mem.New()) 19 | 20 | k, err := New(kms, mem.New()) 21 | assert.NoError(t, err) 22 | 23 | icp, err := k.Inception() 24 | assert.NoError(t, err) 25 | 26 | assert.Equal(t, "Eh0fefvTQ55Jwps4dVnIekf7mZgWoU8bCUsDsKeGiEgU", icp.Event.Prefix) 27 | assert.Equal(t, "D69EflciVP9zgsihNU14Dbm2bPXoNGxKHK_BBVFMQ-YU", icp.Event.Keys[0]) 28 | assert.Equal(t, "E2N7cav-AXF8R86YPUWqo8oGu2YcdyFz_w6lTiNmmOY4", icp.Event.Next) 29 | assert.Equal(t, "Eh0fefvTQ55Jwps4dVnIekf7mZgWoU8bCUsDsKeGiEgU", k.Prefix()) 30 | } 31 | 32 | func TestSign(t *testing.T) { 33 | secrets := []string{"ADW3o9m3udwEf0aoOdZLLJdf1aylokP0lwwI_M2J9h0s", "AagumsL8FeGES7tYcnr_5oN6qcwJzZfLKxoniKUpG4qc"} 34 | kms := testkms.GetKMS(t, secrets, mem.New()) 35 | 36 | k, err := New(kms, mem.New()) 37 | assert.NoError(t, err) 38 | 39 | sig, err := k.Sign([]byte("this is test data")) 40 | assert.NoError(t, err) 41 | assert.Equal(t, "huHZ3nrg6FjhF4eKG4SoachALGHN5B0/dqM9Xchf6DMLA17sIjGBOTz9E4l34o/LqqeAbXPR+x7Tz1vWKM9IDw==", base64.StdEncoding.EncodeToString(sig)) 42 | } 43 | 44 | func TestDirectMode(t *testing.T) { 45 | 46 | eveSecrets := []string{"ArwXoACJgOleVZ2PY7kXn7rA0II0mHYDhc6WrBH8fDAc", "A6zz7M08-HQSFq92sJ8KJOT2cZ47x7pXFQLPB0pckB3Q"} 47 | bobSecrets := []string{"ADW3o9m3udwEf0aoOdZLLJdf1aylokP0lwwI_M2J9h0s", "AagumsL8FeGES7tYcnr_5oN6qcwJzZfLKxoniKUpG4qc", "AagumsL8FeGES7tYcnr_5oN6qcwJzZfLKxoniKUpG4qc"} 48 | 49 | t.Run("no wait", func(t *testing.T) { 50 | 51 | eveKms := testkms.GetKMS(t, eveSecrets, mem.New()) 52 | eve, err := New(eveKms, mem.New()) 53 | assert.NoError(t, err) 54 | 55 | bobKms := testkms.GetKMS(t, bobSecrets, mem.New()) 56 | bob, err := New(bobKms, mem.New()) 57 | assert.NoError(t, err) 58 | 59 | icp, err := bob.Inception() 60 | assert.NoError(t, err) 61 | 62 | //Send bob's icp to Eve and get back icp and receipt 63 | msgsToBob, err := eve.ProcessEvents(icp) 64 | assert.NoError(t, err) 65 | 66 | eveICP, err := eve.Inception() 67 | assert.NoError(t, err) 68 | 69 | msgsToBob = append([]*event.Message{eveICP}, msgsToBob...) 70 | 71 | //Send Eve's icp and vrc to Bob 72 | msgsToEve, err := bob.ProcessEvents(msgsToBob...) 73 | 74 | assert.Len(t, msgsToEve, 1) 75 | 76 | rot, err := bob.Rotate() 77 | 78 | //Duplicitious events not yet handled 79 | msgsToBob, err = eve.ProcessEvents(icp) 80 | assert.NoError(t, err) 81 | 82 | //Send bob's icp to Eve and get back icp and receipt 83 | msgsToBob, err = eve.ProcessEvents(rot) 84 | assert.NoError(t, err) 85 | assert.Len(t, msgsToBob, 1) 86 | 87 | assert.Equal(t, "Eh0fefvTQ55Jwps4dVnIekf7mZgWoU8bCUsDsKeGiEgU", rot.Event.Prefix) 88 | assert.Equal(t, "E2N7cav-AXF8R86YPUWqo8oGu2YcdyFz_w6lTiNmmOY4", rot.Event.Next) 89 | }) 90 | 91 | t.Run("wait", func(t *testing.T) { 92 | 93 | eveKms := testkms.GetKMS(t, eveSecrets, mem.New()) 94 | eve, err := New(eveKms, mem.New()) 95 | assert.NoError(t, err) 96 | 97 | bobKms := testkms.GetKMS(t, bobSecrets, mem.New()) 98 | bob, err := New(bobKms, mem.New()) 99 | assert.NoError(t, err) 100 | 101 | icp, err := bob.Inception() 102 | assert.NoError(t, err) 103 | 104 | rcpts, _ := bob.WaitForReceipt(icp.Event, 5*time.Second) 105 | 106 | //Send bob's icp to Eve and get back icp and receipt 107 | msgsToBob, err := eve.ProcessEvents(icp) 108 | assert.NoError(t, err) 109 | assert.Len(t, msgsToBob, 1) 110 | 111 | eveICP, err := eve.Inception() 112 | assert.NoError(t, err) 113 | 114 | msgsToBob = append([]*event.Message{eveICP}, msgsToBob...) 115 | 116 | //Send Eve's icp and vrc to Bob 117 | msgsToEve, err := bob.ProcessEvents(msgsToBob...) 118 | 119 | rcpt := <-rcpts 120 | 121 | dig, err := icp.Event.GetDigest() 122 | assert.NoError(t, err) 123 | assert.Equal(t, rcpt.EventDigest, dig) 124 | 125 | assert.Len(t, msgsToEve, 1) 126 | 127 | rot, err := bob.Rotate() 128 | 129 | //Duplicitious events not yet handled 130 | msgsToBob, err = eve.ProcessEvents(icp) 131 | assert.NoError(t, err) 132 | 133 | //Send bob's icp to Eve and get back icp and receipt 134 | msgsToBob, err = eve.ProcessEvents(rot) 135 | assert.NoError(t, err) 136 | assert.Len(t, msgsToBob, 1) 137 | 138 | assert.Equal(t, "Eh0fefvTQ55Jwps4dVnIekf7mZgWoU8bCUsDsKeGiEgU", rot.Event.Prefix) 139 | assert.Equal(t, "E2N7cav-AXF8R86YPUWqo8oGu2YcdyFz_w6lTiNmmOY4", rot.Event.Next) 140 | }) 141 | 142 | } 143 | 144 | func TestInteractionEvent(t *testing.T) { 145 | expectedBytes := `{"v":"KERI10JSON000098_","i":"Eh0fefvTQ55Jwps4dVnIekf7mZgWoU8bCUsDsKeGiEgU","s":"1","t":"ixn","p":"EUO96wFpqn7NQgDqRybT1ADVgaony353BSIOkJwdBFSE","a":[]}-AABAAvle4YOvsulhpBC3PbZRe3hNF2JaVDUMlzLaiIk61Puaizy2jCYuoM3ycgM-v0VqKGDrSNbBFXxyVSYSesMhgDw` 146 | secrets := []string{"ADW3o9m3udwEf0aoOdZLLJdf1aylokP0lwwI_M2J9h0s", "AagumsL8FeGES7tYcnr_5oN6qcwJzZfLKxoniKUpG4qc"} 147 | kms := testkms.GetKMS(t, secrets, mem.New()) 148 | 149 | k, err := New(kms, mem.New()) 150 | assert.NoError(t, err) 151 | 152 | ixn, err := k.Interaction([]*event.Seal{}) 153 | d, err := stream.ToDisjoint(ixn) 154 | assert.NoError(t, err) 155 | 156 | assert.Equal(t, expectedBytes, string(d)) 157 | 158 | } 159 | 160 | func TestFindConnection(t *testing.T) { 161 | secrets := []string{"ADW3o9m3udwEf0aoOdZLLJdf1aylokP0lwwI_M2J9h0s", "AagumsL8FeGES7tYcnr_5oN6qcwJzZfLKxoniKUpG4qc"} 162 | 163 | eveSecrets := []string{"ArwXoACJgOleVZ2PY7kXn7rA0II0mHYDhc6WrBH8fDAc", "A6zz7M08-HQSFq92sJ8KJOT2cZ47x7pXFQLPB0pckB3Q"} 164 | bobSecrets := []string{"ADW3o9m3udwEf0aoOdZLLJdf1aylokP0lwwI_M2J9h0s", "AagumsL8FeGES7tYcnr_5oN6qcwJzZfLKxoniKUpG4qc"} 165 | 166 | t.Run("no wait", func(t *testing.T) { 167 | 168 | eveKms := testkms.GetKMS(t, eveSecrets, mem.New()) 169 | eve, err := New(eveKms, mem.New()) 170 | assert.NoError(t, err) 171 | 172 | bobKms := testkms.GetKMS(t, bobSecrets, mem.New()) 173 | bob, err := New(bobKms, mem.New()) 174 | assert.NoError(t, err) 175 | 176 | icp, err := bob.Inception() 177 | assert.NoError(t, err) 178 | 179 | //Send bob's icp to Eve and get back icp and receipt 180 | _, err = eve.ProcessEvents(icp) 181 | assert.NoError(t, err) 182 | 183 | l, err := eve.FindConnection(icp.Event.Prefix) 184 | assert.NoError(t, err) 185 | assert.NotNil(t, l, 1) 186 | }) 187 | 188 | t.Run("not found", func(t *testing.T) { 189 | kms := testkms.GetKMS(t, secrets, mem.New()) 190 | 191 | k, err := New(kms, mem.New()) 192 | assert.NoError(t, err) 193 | 194 | l, err := k.FindConnection("bad prefix") 195 | assert.Error(t, err) 196 | assert.Nil(t, l) 197 | }) 198 | 199 | } 200 | -------------------------------------------------------------------------------- /pkg/event/main.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/decentralized-identity/kerigo/pkg/derivation" 8 | "github.com/decentralized-identity/kerigo/pkg/prefix" 9 | "github.com/decentralized-identity/kerigo/pkg/version" 10 | ) 11 | 12 | type EventOption func(*Event) error 13 | 14 | // WithKeys sets the keys that are applicable for the event 15 | func WithKeys(keys ...prefix.Prefix) EventOption { 16 | return func(e *Event) error { 17 | for i := 0; i < len(keys); i++ { 18 | k := keys[i].String() 19 | e.Keys = append(e.Keys, k) 20 | } 21 | return nil 22 | } 23 | } 24 | 25 | // WithWitnesses sets the witness keys for the event 26 | func WithWitnesses(keys ...prefix.Prefix) EventOption { 27 | return func(e *Event) error { 28 | for i := 0; i < len(keys); i++ { 29 | k := keys[i].String() 30 | e.Witnesses = append(e.Keys, k) 31 | } 32 | return nil 33 | } 34 | } 35 | 36 | // WithNext keys must be self adressing prefixs. Do not use a basic prefix 37 | // otherwise the public key data will be exposed in the log breaking post-quantum 38 | // security. 39 | // To support multi-sig, next is a prefix of the commitment to a signing threshold 40 | // along with all of the keys to be rotated to, combined using XOR. 41 | // Each of the provided keys, along with the derivation to use for the next, 42 | // must use the same derivaiton code. 43 | func WithNext(threshold string, code derivation.Code, keys ...prefix.Prefix) EventOption { 44 | return func(e *Event) error { 45 | next, err := NextDigest(threshold, code, keys...) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | e.Next = next 51 | return nil 52 | } 53 | } 54 | 55 | // WithThreshold sets the key threshold 56 | func WithThreshold(threshold int64) EventOption { 57 | return func(e *Event) error { 58 | st, err := NewSigThreshold(threshold) 59 | if err != nil { 60 | return err 61 | } 62 | e.SigThreshold = st 63 | return nil 64 | } 65 | } 66 | 67 | // WithWeightedTheshold sets a weighted signing threshold using provided 68 | // string int or fraction values. The total for all conditions must be 69 | // >= 1 otherwise the threshold can not be met. The order in which 70 | // the conditions are provided is important: they map to the specific 71 | // key index in the keys list, e.g, the second condition provided to this 72 | // configuration function would be the weight of a signature by the second key 73 | // in the keys list. 74 | func WithWeightedTheshold(conditions ...string) EventOption { 75 | return func(e *Event) error { 76 | st, err := NewWeighted(conditions...) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | e.SigThreshold = st 82 | 83 | return nil 84 | } 85 | } 86 | 87 | // WithMultiWeightedThesholds sets multiple weighted signing thresholds using provided 88 | // string values. 89 | func WithMultiWeightedThesholds(thresholds ...[]string) EventOption { 90 | return func(e *Event) error { 91 | st, err := NewMultiWeighted(thresholds...) 92 | if err != nil { 93 | return err 94 | } 95 | 96 | e.SigThreshold = st 97 | 98 | return nil 99 | } 100 | } 101 | 102 | // WithWitnessThreshold sets the witness duplicity threshold for the event 103 | func WithWitnessThreshold(threshold int) EventOption { 104 | return func(e *Event) error { 105 | e.WitnessThreshold = fmt.Sprintf("%x", threshold) 106 | return nil 107 | } 108 | } 109 | 110 | // WithType specifies the event type 111 | func WithType(eventType ILK) EventOption { 112 | return func(e *Event) error { 113 | e.EventType = ilkString[eventType] 114 | return nil 115 | } 116 | } 117 | 118 | /// WithSequence sets the sequence number for this event 119 | func WithSequence(sequence int) EventOption { 120 | return func(e *Event) error { 121 | e.Sequence = fmt.Sprintf("%x", sequence) 122 | return nil 123 | } 124 | } 125 | 126 | // WithDigest sets the digest for the event 127 | func WithDigest(digest string) EventOption { 128 | return func(e *Event) error { 129 | e.PriorEventDigest = digest 130 | return nil 131 | } 132 | } 133 | 134 | func WithDefaultVersion(in FORMAT) EventOption { 135 | return func(e *Event) error { 136 | e.Version = DefaultVersionString(in) 137 | return nil 138 | } 139 | } 140 | 141 | func WithPrefix(prefix string) EventOption { 142 | return func(e *Event) error { 143 | e.Prefix = prefix 144 | return nil 145 | } 146 | } 147 | 148 | func WithSeals(seals SealArray) EventOption { 149 | return func(e *Event) error { 150 | e.Seals = seals 151 | return nil 152 | } 153 | } 154 | 155 | // NewEvent returns a new event with the specified options applied 156 | func NewEvent(opts ...EventOption) (*Event, error) { 157 | st, _ := NewSigThreshold(1) 158 | e := &Event{ 159 | Sequence: "0", 160 | SigThreshold: st, 161 | WitnessThreshold: "0", 162 | Witnesses: []string{}, 163 | Config: []prefix.Trait{}, 164 | } 165 | 166 | for _, o := range opts { 167 | err := o(e) 168 | if err != nil { 169 | return nil, err 170 | } 171 | } 172 | 173 | if e.EventType == "" { 174 | return nil, errors.New("must specify an event type") 175 | } 176 | 177 | if (e.EventType != ilkString[ICP] && e.EventType != ilkString[VRC] && e.EventType != ilkString[RCT]) && e.Sequence == "0" { 178 | return nil, errors.New("only inception events may have a sequence of 0") 179 | } 180 | 181 | return e, nil 182 | } 183 | 184 | // NewInceptionEvent returns and incpetion configured with the provided parameters 185 | // New Inception Events will have empty 'v' and 'i' strings 186 | func NewInceptionEvent(opts ...EventOption) (*Event, error) { 187 | st, _ := NewSigThreshold(1) 188 | e := &Event{ 189 | EventType: ilkString[ICP], 190 | Sequence: "0", 191 | SigThreshold: st, 192 | WitnessThreshold: "0", 193 | Witnesses: []string{}, 194 | Config: []prefix.Trait{}, 195 | } 196 | for _, o := range opts { 197 | err := o(e) 198 | if err != nil { 199 | return nil, err 200 | } 201 | } 202 | 203 | return e, nil 204 | } 205 | 206 | // NewRotationEvent returns and incpetion configured with the provided parameters 207 | // New Rotation Events will have empty 'v' and 'i' strings 208 | func NewRotationEvent(opts ...EventOption) (*Event, error) { 209 | sith, _ := NewSigThreshold(1) 210 | rot := &Event{ 211 | EventType: ilkString[ROT], 212 | Sequence: "0", 213 | SigThreshold: sith, 214 | WitnessThreshold: "0", 215 | Witnesses: []string{}, 216 | Config: []prefix.Trait{}, 217 | } 218 | for _, o := range opts { 219 | err := o(rot) 220 | if err != nil { 221 | return nil, err 222 | } 223 | } 224 | 225 | if rot.Prefix == "" { 226 | return nil, errors.New("prefix required for rot") 227 | } 228 | 229 | if rot.Next == "" { 230 | return nil, errors.New("next commitment required for rot") 231 | } 232 | 233 | // Serialize with defaults to get correct length for version string 234 | if rot.Version == "" { 235 | rot.Version = DefaultVersionString(JSON) 236 | } 237 | 238 | eventBytes, err := Serialize(rot, JSON) 239 | if err != nil { 240 | return nil, err 241 | } 242 | 243 | rot.Version = VersionString(JSON, version.Code(), len(eventBytes)) 244 | 245 | return rot, nil 246 | } 247 | 248 | // NewRotationEvent returns and incpetion configured with the provided parameters 249 | // New Rotation Events will have empty 'v' and 'i' strings 250 | func NewInteractionEvent(opts ...EventOption) (*Event, error) { 251 | rot := &Event{ 252 | EventType: ilkString[IXN], 253 | Sequence: "0", 254 | } 255 | for _, o := range opts { 256 | err := o(rot) 257 | if err != nil { 258 | return nil, err 259 | } 260 | } 261 | 262 | if rot.Prefix == "" { 263 | return nil, errors.New("prefix required for ixn") 264 | } 265 | 266 | // Serialize with defaults to get correct length for version string 267 | rot.Version = DefaultVersionString(JSON) 268 | eventBytes, err := Serialize(rot, JSON) 269 | if err != nil { 270 | return nil, err 271 | } 272 | 273 | rot.Version = VersionString(JSON, version.Code(), len(eventBytes)) 274 | 275 | return rot, nil 276 | } 277 | -------------------------------------------------------------------------------- /cmd/demo/joe/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | 9 | "github.com/google/tink/go/aead" 10 | "github.com/google/tink/go/keyset" 11 | 12 | kbdgr "github.com/decentralized-identity/kerigo/pkg/db/badger" 13 | "github.com/decentralized-identity/kerigo/pkg/encoding/stream" 14 | "github.com/decentralized-identity/kerigo/pkg/event" 15 | "github.com/decentralized-identity/kerigo/pkg/keri" 16 | "github.com/decentralized-identity/kerigo/pkg/keymanager" 17 | ) 18 | 19 | var ( 20 | secrets = []string{ 21 | "ArwXoACJgOleVZ2PY7kXn7rA0II0mHYDhc6WrBH8fDAc", 22 | "A6zz7M08-HQSFq92sJ8KJOT2cZ47x7pXFQLPB0pckB3Q", 23 | "AcwFTk-wgk3ZT2buPRIbK-zxgPx-TKbaegQvPEivN90Y", 24 | "Alntkt3u6dDgiQxTATr01dy8M72uuaZEf9eTdM-70Gk8", 25 | "A1-QxDkso9-MR1A8rZz_Naw6fgaAtayda8hrbkRVVu1E", 26 | "AKuYMe09COczwf2nIoD5AE119n7GLFOVFlNLxZcKuswc", 27 | "AxFfJTcSuEE11FINfXMqWttkZGnUZ8KaREhrnyAXTsjw", 28 | "ALq-w1UKkdrppwZzGTtz4PWYEeWm0-sDHzOv5sq96xJY", 29 | } 30 | ) 31 | 32 | func main() { 33 | 34 | goodGuy := createGoodGuy() 35 | badGuy := createBadGuy() 36 | innocent := createInnocent() 37 | 38 | count := 0 39 | err := goodGuy.Replay(goodGuy.Prefix(), keri.FirstSeenReplay, func(e *event.Message) error { 40 | _, err := innocent.ProcessEvents(e) 41 | count++ 42 | return err 43 | }) 44 | fmt.Printf("Good Guy replayed first %d events\n", count) 45 | 46 | if err != nil { 47 | log.Fatalf("innocent error processing good guy %v\n", err) 48 | } 49 | 50 | //IXN 51 | ixn, err := badGuy.Interaction([]*event.Seal{}) 52 | if err != nil { 53 | log.Fatalln(err) 54 | } 55 | 56 | dig, _ := ixn.Event.GetDigest() 57 | fmt.Println("Bad Guy duplicitious IXN", dig) 58 | _, err = innocent.ProcessEvents(ixn) 59 | if err != nil { 60 | log.Fatalf("%+v\n", err) 61 | } 62 | 63 | //IXN 64 | ixn, err = badGuy.Interaction([]*event.Seal{}) 65 | if err != nil { 66 | log.Fatalf("%+v\n", err) 67 | } 68 | 69 | dig, _ = ixn.Event.GetDigest() 70 | fmt.Println("Bad Guy 2nd duplicitious IXN", dig) 71 | _, err = innocent.ProcessEvents(ixn) 72 | if err != nil { 73 | log.Fatalf("%+v\n", err) 74 | } 75 | 76 | //IXN 77 | ixn, err = badGuy.Interaction([]*event.Seal{}) 78 | if err != nil { 79 | log.Fatalf("%+v\n", err) 80 | } 81 | 82 | dig, _ = ixn.Event.GetDigest() 83 | fmt.Println("Bad Guy 3rd duplicitious IXN", dig) 84 | _, err = innocent.ProcessEvents(ixn) 85 | if err != nil { 86 | log.Fatalf("%+v\n", err) 87 | } 88 | 89 | rot, err := goodGuy.Rotate() 90 | if err != nil { 91 | log.Fatalf("%+v\n", err) 92 | } 93 | 94 | dig, _ = rot.Event.GetDigest() 95 | fmt.Println("Good Guy recover ROT", dig) 96 | _, err = innocent.ProcessEvents(rot) 97 | if err != nil { 98 | log.Fatalf("%+v\n", err) 99 | } 100 | 101 | fmt.Println("****************************************************") 102 | fmt.Println("*************** First Seen Replay ******************") 103 | fmt.Println("****************************************************") 104 | err = innocent.Replay(goodGuy.Prefix(), keri.FirstSeenReplay, func(e *event.Message) error { 105 | dig, _ := e.Event.GetDigest() 106 | fmt.Println(e.Event.EventType, e.Event.Sequence, "-", dig) 107 | return nil 108 | }) 109 | fmt.Println("****************************************************") 110 | fmt.Println("") 111 | 112 | fmt.Println("****************************************************") 113 | fmt.Println("***************** Seq No Replay ********************") 114 | fmt.Println("****************************************************") 115 | err = innocent.Replay(goodGuy.Prefix(), keri.SequenceNumberReplay, func(e *event.Message) error { 116 | dig, _ := e.Event.GetDigest() 117 | fmt.Println(e.Event.EventType, e.Event.Sequence, "-", dig) 118 | return nil 119 | }) 120 | fmt.Println("****************************************************") 121 | fmt.Println("") 122 | 123 | fmt.Println("") 124 | 125 | fmt.Println("****************************************************") 126 | fmt.Println("************** First Seen Conjoint *****************") 127 | fmt.Println("****************************************************") 128 | enc := stream.NewWriter(os.Stdout, stream.WithSerializationMode(stream.ConjointMode)) 129 | err = innocent.Replay(goodGuy.Prefix(), keri.FirstSeenReplay, func(e *event.Message) error { 130 | return enc.Write(e) 131 | }) 132 | fmt.Println("") 133 | fmt.Println("****************************************************") 134 | 135 | if err != nil { 136 | log.Fatalf("%+v\n", err) 137 | } 138 | } 139 | 140 | func createInnocent() *keri.Keri { 141 | td, err := ioutil.TempDir("", "keri-innocent-*") 142 | if err != nil { 143 | log.Fatalln(err) 144 | } 145 | 146 | db, err := kbdgr.New(td) 147 | if err != nil { 148 | log.Fatalln(err) 149 | } 150 | 151 | kh, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) 152 | if err != nil { 153 | log.Fatalln(err) 154 | } 155 | 156 | a, err := aead.New(kh) 157 | if err != nil { 158 | log.Fatalln(err) 159 | } 160 | 161 | km, err := keymanager.NewKeyManager(keymanager.WithAEAD(a), keymanager.WithStore(db)) 162 | if err != nil { 163 | log.Fatalln(err) 164 | } 165 | 166 | //ICP 167 | kerl, err := keri.New(km, db) 168 | if err != nil { 169 | log.Fatalln(err) 170 | } 171 | 172 | return kerl 173 | } 174 | 175 | func createGoodGuy() *keri.Keri { 176 | td, err := ioutil.TempDir("", "keri-good-*") 177 | if err != nil { 178 | log.Fatalln(err) 179 | } 180 | 181 | db, err := kbdgr.New(td) 182 | if err != nil { 183 | log.Fatalln(err) 184 | } 185 | 186 | kh, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) 187 | if err != nil { 188 | log.Fatalln(err) 189 | } 190 | 191 | a, err := aead.New(kh) 192 | if err != nil { 193 | log.Fatalln(err) 194 | } 195 | 196 | km, err := keymanager.NewKeyManager(keymanager.WithAEAD(a), keymanager.WithStore(db), keymanager.WithSecrets(secrets)) 197 | if err != nil { 198 | log.Fatalln(err) 199 | } 200 | 201 | //ICP 202 | kerl, err := keri.New(km, db) 203 | if err != nil { 204 | log.Fatalln(err) 205 | } 206 | 207 | fmt.Println("Good Guy Prefix:", kerl.Prefix()) 208 | 209 | //ROT 210 | _, err = kerl.Rotate() 211 | if err != nil { 212 | log.Fatalln(err) 213 | } 214 | 215 | //IXN 216 | _, err = kerl.Interaction([]*event.Seal{}) 217 | if err != nil { 218 | log.Fatalln(err) 219 | } 220 | 221 | //IXN 222 | _, err = kerl.Interaction([]*event.Seal{}) 223 | if err != nil { 224 | log.Fatalln(err) 225 | } 226 | 227 | //ROT 228 | _, err = kerl.Rotate() 229 | if err != nil { 230 | log.Fatalln(err) 231 | } 232 | 233 | //IXN 234 | _, err = kerl.Interaction([]*event.Seal{}) 235 | if err != nil { 236 | log.Fatalln(err) 237 | } 238 | 239 | return kerl 240 | 241 | } 242 | 243 | func createBadGuy() *keri.Keri { 244 | td, err := ioutil.TempDir("", "keri-bad-*") 245 | if err != nil { 246 | log.Fatalln(err) 247 | } 248 | 249 | db, err := kbdgr.New(td) 250 | if err != nil { 251 | log.Fatalln(err) 252 | } 253 | 254 | kh, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) 255 | if err != nil { 256 | log.Fatalln(err) 257 | } 258 | 259 | a, err := aead.New(kh) 260 | if err != nil { 261 | log.Fatalln(err) 262 | } 263 | 264 | km, err := keymanager.NewKeyManager(keymanager.WithAEAD(a), keymanager.WithStore(db), keymanager.WithSecrets(secrets)) 265 | if err != nil { 266 | log.Fatalln(err) 267 | } 268 | 269 | //ICP 270 | kerl, err := keri.New(km, db) 271 | if err != nil { 272 | log.Fatalln(err) 273 | } 274 | 275 | fmt.Println("Bad Guy Prefix:", kerl.Prefix()) 276 | 277 | //ROT 278 | _, err = kerl.Rotate() 279 | if err != nil { 280 | log.Fatalln(err) 281 | } 282 | 283 | //IXN 284 | _, err = kerl.Interaction([]*event.Seal{}) 285 | if err != nil { 286 | log.Fatalln(err) 287 | } 288 | 289 | //IXN 290 | _, err = kerl.Interaction([]*event.Seal{}) 291 | if err != nil { 292 | log.Fatalln(err) 293 | } 294 | 295 | //ROT 296 | _, err = kerl.Rotate() 297 | if err != nil { 298 | log.Fatalln(err) 299 | } 300 | 301 | //IXN 302 | _, err = kerl.Interaction([]*event.Seal{}) 303 | if err != nil { 304 | log.Fatalln(err) 305 | } 306 | 307 | return kerl 308 | } 309 | -------------------------------------------------------------------------------- /pkg/event/sig-threshold_test.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "encoding/json" 5 | "math/big" 6 | "testing" 7 | 8 | "github.com/decentralized-identity/kerigo/pkg/derivation" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestSigThreshold(t *testing.T) { 13 | assert := assert.New(t) 14 | 15 | st, err := NewSigThreshold(-1) 16 | assert.Nil(st) 17 | assert.Equal("threshold must be >= 0", err.Error()) 18 | 19 | st, err = NewSigThreshold(0) 20 | assert.Nil(err) 21 | assert.Equal([][]*big.Rat{{big.NewRat(0, 1)}}, st.conditions) 22 | 23 | st, err = NewSigThreshold(1) 24 | assert.Nil(err) 25 | assert.Equal([][]*big.Rat{{big.NewRat(1, 1)}}, st.conditions) 26 | 27 | st, err = NewSigThreshold(4) 28 | assert.Nil(err) 29 | assert.Equal([][]*big.Rat{{big.NewRat(4, 1)}}, st.conditions) 30 | 31 | // This is a "simple" threshold, i.e. not weighted 32 | assert.False(st.Weighted()) 33 | 34 | // thresholds must be > 1 35 | st, err = NewWeighted() 36 | assert.Nil(st) 37 | assert.Equal("threshold not satifiable: sum of weights must be greather than 1", err.Error()) 38 | 39 | st, err = NewWeighted("1/2") 40 | assert.Nil(st) 41 | assert.Equal("threshold not satifiable: sum of weights must be greather than 1", err.Error()) 42 | 43 | st, err = NewWeighted("1/2", "1/4") 44 | assert.Nil(st) 45 | assert.Equal("threshold not satifiable: sum of weights must be greather than 1", err.Error()) 46 | 47 | // Invalid fractional representation 48 | st, err = NewWeighted("asdf") 49 | assert.Nil(st) 50 | assert.Equal("unable to parse condition asdf: Rat.Scan: invalid syntax", err.Error()) 51 | 52 | // No negative weights 53 | st, err = NewWeighted("1/4", "1/2", "-1/4") 54 | assert.Nil(st) 55 | assert.Equal("thresholds must be >= 0", err.Error()) 56 | 57 | // different combinations that combined are >1 58 | st, err = NewWeighted("1/2", "1/2") 59 | assert.Nil(err) 60 | if assert.Len(st.conditions, 1) { 61 | assert.Len(st.conditions[0], 2) 62 | assert.Contains(st.conditions[0], big.NewRat(1, 2)) 63 | } 64 | 65 | st, err = NewWeighted("1/2", "1/4", "1/4") 66 | assert.Nil(err) 67 | if assert.Len(st.conditions, 1) { 68 | assert.Len(st.conditions[0], 3) 69 | assert.Contains(st.conditions[0], big.NewRat(1, 2)) 70 | assert.Contains(st.conditions[0], big.NewRat(1, 4)) 71 | } 72 | 73 | st, err = NewWeighted("1/2", "1/4", "1/4", "1") 74 | assert.Nil(err) 75 | if assert.Len(st.conditions, 1) { 76 | assert.Len(st.conditions[0], 4) 77 | assert.Contains(st.conditions[0], big.NewRat(1, 2)) 78 | assert.Contains(st.conditions[0], big.NewRat(1, 4)) 79 | assert.Contains(st.conditions[0], big.NewRat(1, 1)) 80 | } 81 | 82 | // this is a weighted threshold 83 | assert.True(st.Weighted()) 84 | 85 | // All thresholds must be staisfiable since they are treated as "AND" condiitons 86 | st, err = NewMultiWeighted([]string{"1/2", "1/4"}, []string{"1"}) 87 | assert.Nil(st) 88 | assert.Equal("threshold not statisfiable ([1/2 1/4])", err.Error()) 89 | 90 | st, err = NewMultiWeighted([]string{"1/2", "1/4", "1/4"}, []string{"-1"}) 91 | assert.Nil(st) 92 | assert.Equal("thresholds must be >= 0", err.Error()) 93 | 94 | st, err = NewMultiWeighted([]string{"1/2", "1/4", "1/4"}, []string{"1"}) 95 | assert.Nil(err) 96 | if assert.Len(st.conditions, 2) { 97 | assert.Len(st.conditions[0], 3) 98 | assert.Contains(st.conditions[0], big.NewRat(1, 2)) 99 | assert.Contains(st.conditions[0], big.NewRat(1, 4)) 100 | 101 | assert.Len(st.conditions[1], 1) 102 | assert.Contains(st.conditions[1], big.NewRat(1, 1)) 103 | } 104 | 105 | // this is a weighted threshold 106 | assert.True(st.Weighted()) 107 | 108 | } 109 | 110 | func TestJSONMarshalSigThreshold(t *testing.T) { 111 | assert := assert.New(t) 112 | 113 | st, _ := NewSigThreshold(1) 114 | j, err := json.Marshal(st) 115 | assert.Nil(err) 116 | assert.Equal([]byte(`"1"`), j) 117 | 118 | st, _ = NewSigThreshold(25) 119 | j, err = json.Marshal(st) 120 | assert.Nil(err) 121 | assert.Equal([]byte(`"25"`), j) 122 | 123 | st = &SigThreshold{} 124 | j, err = json.Marshal(st) 125 | assert.Nil(err) 126 | assert.Equal([]byte(`"0"`), j) 127 | 128 | st, _ = NewWeighted("1", "1/2", "1/4") 129 | j, err = json.Marshal(st) 130 | assert.Nil(err) 131 | assert.Equal([]byte(`["1","1/2","1/4"]`), j) 132 | 133 | st, _ = NewWeighted("1") 134 | j, err = json.Marshal(st) 135 | assert.Nil(err) 136 | assert.Equal([]byte(`"1"`), j) 137 | 138 | st, _ = NewMultiWeighted([]string{"1", "1/4", "1/2"}, []string{"1"}) 139 | j, err = json.Marshal(st) 140 | assert.Nil(err) 141 | assert.Equal([]byte(`[["1","1/4","1/2"],["1"]]`), j) 142 | 143 | st, _ = NewMultiWeighted([]string{"1", "1/4", "1/2"}) 144 | j, err = json.Marshal(st) 145 | assert.Nil(err) 146 | assert.Equal([]byte(`["1","1/4","1/2"]`), j) 147 | } 148 | 149 | func TestJSONUnmarshalSigThreshold(t *testing.T) { 150 | assert := assert.New(t) 151 | 152 | st := &SigThreshold{} 153 | err := json.Unmarshal([]byte(`"1"`), st) 154 | 155 | assert.Nil(err) 156 | if assert.Len(st.conditions, 1) { 157 | assert.Len(st.conditions[0], 1) 158 | assert.Contains(st.conditions[0], big.NewRat(1, 1)) 159 | } 160 | 161 | st = &SigThreshold{} 162 | err = json.Unmarshal([]byte(`["1"]`), st) 163 | 164 | assert.Nil(err) 165 | if assert.Len(st.conditions, 1) { 166 | assert.Len(st.conditions[0], 1) 167 | assert.Contains(st.conditions[0], big.NewRat(1, 1)) 168 | } 169 | 170 | st = &SigThreshold{} 171 | err = json.Unmarshal([]byte(`["1","1/4","1/2"]`), st) 172 | assert.Nil(err) 173 | if assert.Len(st.conditions, 1) { 174 | assert.Len(st.conditions[0], 3) 175 | assert.Contains(st.conditions[0], big.NewRat(1, 1)) 176 | assert.Contains(st.conditions[0], big.NewRat(1, 4)) 177 | assert.Contains(st.conditions[0], big.NewRat(1, 2)) 178 | } 179 | 180 | st = &SigThreshold{} 181 | err = json.Unmarshal([]byte(`[["1","1/4","1/2"],["1"]]`), st) 182 | assert.Nil(err) 183 | if assert.Len(st.conditions, 2) { 184 | assert.Len(st.conditions[0], 3) 185 | assert.Contains(st.conditions[0], big.NewRat(1, 1)) 186 | assert.Contains(st.conditions[0], big.NewRat(1, 4)) 187 | assert.Contains(st.conditions[0], big.NewRat(1, 2)) 188 | assert.Len(st.conditions[1], 1) 189 | assert.Contains(st.conditions[0], big.NewRat(1, 1)) 190 | } 191 | 192 | // if you throw bad stuff at us, we do our best to ignore your stupidity 193 | // (i.e. pick one way and go with it - either weighted, or multi-weighted, but 194 | // you cantz have both) 195 | st = &SigThreshold{} 196 | err = json.Unmarshal([]byte(`[["1","1/4","1/2"],"1"]`), st) 197 | assert.Nil(err) 198 | if assert.Len(st.conditions, 1) { 199 | assert.Len(st.conditions[0], 3) 200 | assert.Contains(st.conditions[0], big.NewRat(1, 1)) 201 | assert.Contains(st.conditions[0], big.NewRat(1, 4)) 202 | assert.Contains(st.conditions[0], big.NewRat(1, 2)) 203 | } 204 | 205 | st = &SigThreshold{} 206 | err = json.Unmarshal([]byte(`["1", ["1","1/4","1/2"]]`), st) 207 | assert.Nil(err) 208 | if assert.Len(st.conditions, 1) { 209 | assert.Len(st.conditions[0], 1) 210 | assert.Contains(st.conditions[0], big.NewRat(1, 1)) 211 | } 212 | } 213 | 214 | func TestSatisfied(t *testing.T) { 215 | assert := assert.New(t) 216 | 217 | st, _ := NewSigThreshold(3) 218 | sigs := []derivation.Derivation{{KeyIndex: 0}} 219 | assert.False(st.Satisfied(sigs)) 220 | 221 | sigs = []derivation.Derivation{{KeyIndex: 0}, {KeyIndex: 1}} 222 | assert.False(st.Satisfied(sigs)) 223 | 224 | sigs = []derivation.Derivation{{KeyIndex: 0}, {KeyIndex: 1}, {KeyIndex: 2}} 225 | assert.True(st.Satisfied(sigs)) 226 | 227 | st, _ = NewWeighted("1/2", "1/4", "1/4") 228 | assert.True(st.Satisfied(sigs)) 229 | 230 | sigs = []derivation.Derivation{{KeyIndex: 0}} 231 | assert.False(st.Satisfied(sigs)) 232 | 233 | sigs = []derivation.Derivation{{KeyIndex: 0}, {KeyIndex: 1}, {KeyIndex: 5}} 234 | assert.False(st.Satisfied(sigs)) 235 | 236 | sigs = []derivation.Derivation{{KeyIndex: 2}, {KeyIndex: 1}} 237 | assert.False(st.Satisfied(sigs)) 238 | 239 | st, _ = NewMultiWeighted([]string{"1/2", "1/2", "1/4", "1/4", "1/4", "1/4"}, []string{"1", "1"}) 240 | 241 | // meet the second but not the first 242 | sigs = []derivation.Derivation{{KeyIndex: 1}, {KeyIndex: 4}} 243 | assert.False(st.Satisfied(sigs)) 244 | 245 | // meet the first but not the second 246 | sigs = []derivation.Derivation{{KeyIndex: 2}, {KeyIndex: 3}, {KeyIndex: 4}, {KeyIndex: 5}} 247 | assert.False(st.Satisfied(sigs)) 248 | 249 | // pass 250 | sigs = []derivation.Derivation{{KeyIndex: 0}, {KeyIndex: 2}, {KeyIndex: 3}, {KeyIndex: 4}, {KeyIndex: 5}} 251 | assert.True(st.Satisfied(sigs)) 252 | } 253 | 254 | func TestSigThresholdString(t *testing.T) { 255 | assert := assert.New(t) 256 | 257 | st, _ := NewSigThreshold(1) 258 | assert.Equal("1", st.String()) 259 | 260 | st, _ = NewWeighted("1/2", "1/4", "1/4") 261 | assert.Equal("1/2,1/4,1/4", st.String()) 262 | 263 | st, _ = NewMultiWeighted([]string{"1/2", "1/2", "1/4", "1/4", "1/4", "1/4"}, []string{"1", "1"}, []string{"1"}) 264 | assert.Equal("1/2,1/2,1/4,1/4,1/4,1/4&1,1&1", st.String()) 265 | } 266 | -------------------------------------------------------------------------------- /pkg/db/mem/mem.go: -------------------------------------------------------------------------------- 1 | package mem 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "sync" 7 | 8 | "github.com/decentralized-identity/kerigo/pkg/event" 9 | ) 10 | 11 | type DB struct { 12 | valueLock sync.RWMutex 13 | values map[string][]byte 14 | 15 | logLock sync.RWMutex 16 | logs map[string][][]*event.Message 17 | seen map[string][]*event.Message 18 | 19 | pendLock sync.RWMutex 20 | pending map[string][][]*event.Message 21 | 22 | dupLock sync.RWMutex 23 | likelyDups map[string][][]*event.Message 24 | 25 | rcptLock sync.RWMutex 26 | rcpts map[string][]string 27 | } 28 | 29 | func New() *DB { 30 | return &DB{ 31 | valueLock: sync.RWMutex{}, 32 | values: map[string][]byte{}, 33 | 34 | logLock: sync.RWMutex{}, 35 | logs: map[string][][]*event.Message{}, 36 | seen: map[string][]*event.Message{}, 37 | 38 | pendLock: sync.RWMutex{}, 39 | pending: map[string][][]*event.Message{}, 40 | 41 | dupLock: sync.RWMutex{}, 42 | likelyDups: map[string][][]*event.Message{}, 43 | 44 | rcptLock: sync.RWMutex{}, 45 | rcpts: map[string][]string{}, 46 | } 47 | } 48 | 49 | func (r *DB) Put(k string, v []byte) error { 50 | r.valueLock.Lock() 51 | defer r.valueLock.Unlock() 52 | 53 | r.values[k] = v 54 | return nil 55 | } 56 | 57 | func (r *DB) Get(k string) ([]byte, error) { 58 | r.valueLock.RLock() 59 | defer r.valueLock.RUnlock() 60 | 61 | v, ok := r.values[k] 62 | if !ok { 63 | return nil, errors.New("not found") 64 | } 65 | return v, nil 66 | } 67 | 68 | func (r *DB) LogSize(pre string) int { 69 | r.logLock.RLock() 70 | defer r.logLock.RUnlock() 71 | 72 | l, ok := r.logs[pre] 73 | if !ok { 74 | return 0 75 | } 76 | 77 | return len(l) 78 | } 79 | 80 | func (r *DB) Seen(pre string) bool { 81 | r.logLock.RLock() 82 | defer r.logLock.RUnlock() 83 | 84 | _, ok := r.logs[pre] 85 | return ok 86 | } 87 | 88 | func (r *DB) Inception(pre string) (*event.Message, error) { 89 | r.logLock.RLock() 90 | defer r.logLock.RUnlock() 91 | 92 | l, ok := r.logs[pre] 93 | if !ok || len(l) == 0 { 94 | return nil, errors.New("not found") 95 | } 96 | 97 | return l[0][0], nil 98 | } 99 | 100 | func (r *DB) CurrentEvent(pre string) (*event.Message, error) { 101 | r.logLock.RLock() 102 | defer r.logLock.RUnlock() 103 | 104 | l, ok := r.logs[pre] 105 | if !ok || len(l) == 0 { 106 | return nil, errors.New("not found") 107 | } 108 | 109 | evts := l[len(l)-1] 110 | 111 | return evts[len(evts)-1], nil 112 | } 113 | 114 | func (r *DB) CurrentEstablishmentEvent(pre string) (*event.Message, error) { 115 | r.logLock.RLock() 116 | defer r.logLock.RUnlock() 117 | 118 | var out *event.Message 119 | l := r.logs[pre] 120 | for _, evts := range l { 121 | msg := evts[len(evts)-1] 122 | if msg.Event.IsEstablishment() { 123 | out = msg 124 | } 125 | } 126 | 127 | if out == nil { 128 | return nil, errors.New("not found") 129 | } 130 | 131 | return out, nil 132 | } 133 | 134 | func (r *DB) EventAt(pre string, sequence int) (*event.Message, error) { 135 | r.logLock.RLock() 136 | defer r.logLock.RUnlock() 137 | 138 | l, ok := r.logs[pre] 139 | if !ok || len(l) < sequence-1 || sequence < 0 { 140 | return nil, errors.New("not found") 141 | } 142 | 143 | evts := l[sequence] 144 | return evts[len(evts)-1], nil 145 | } 146 | 147 | func (r *DB) LogEvent(e *event.Message, first bool) error { 148 | r.logLock.Lock() 149 | defer r.logLock.Unlock() 150 | 151 | pre := e.Event.Prefix 152 | l, ok := r.logs[pre] 153 | if !ok { 154 | l := [][]*event.Message{{e}} 155 | r.logs[pre] = l 156 | } else { 157 | sn := e.Event.SequenceInt() 158 | if sn < len(l) { 159 | l[sn] = append(l[sn], e) 160 | } else { 161 | r.logs[pre] = append(l, []*event.Message{e}) 162 | } 163 | } 164 | 165 | s, ok := r.seen[pre] 166 | if !ok { 167 | r.seen[pre] = []*event.Message{e} 168 | } else { 169 | r.seen[pre] = append(s, e) 170 | } 171 | 172 | return nil 173 | } 174 | 175 | func (r *DB) StreamEstablisment(pre string, handler func(*event.Message) error) error { 176 | r.logLock.RLock() 177 | defer r.logLock.RUnlock() 178 | 179 | l, ok := r.logs[pre] 180 | if !ok { 181 | return nil 182 | } 183 | 184 | for _, msg := range l { 185 | evt := msg[len(msg)-1] 186 | if evt.Event.IsEstablishment() { 187 | err := handler(evt) 188 | if err != nil { 189 | return err 190 | } 191 | } 192 | } 193 | 194 | return nil 195 | } 196 | 197 | func (r *DB) Close() error { 198 | return nil 199 | } 200 | 201 | //TODO: implement 202 | func (r *DB) StreamAsFirstSeen(pre string, handler func(*event.Message) error) error { 203 | r.logLock.RLock() 204 | defer r.logLock.RUnlock() 205 | 206 | log, ok := r.seen[pre] 207 | if !ok { 208 | return errors.New("not found") 209 | } 210 | 211 | for _, evt := range log { 212 | err := handler(evt) 213 | if err != nil { 214 | return err 215 | } 216 | } 217 | 218 | return nil 219 | } 220 | 221 | //TODO: implement 222 | func (r *DB) StreamBySequenceNo(pre string, handler func(*event.Message) error) error { 223 | r.logLock.RLock() 224 | defer r.logLock.RUnlock() 225 | 226 | log, ok := r.logs[pre] 227 | if !ok { 228 | return errors.New("not found") 229 | } 230 | 231 | fork := []byte{} 232 | for _, evts := range log { 233 | evt := evts[len(evts)-1] 234 | 235 | if len(fork) != 0 { 236 | if bytes.Compare([]byte(evt.Event.PriorEventDigest), fork) != 0 { 237 | break 238 | } 239 | } 240 | 241 | if len(evts) > 1 { 242 | dig, _ := evt.Event.GetDigest() 243 | fork = []byte(dig) 244 | } else { 245 | fork = []byte{} 246 | } 247 | 248 | err := handler(evt) 249 | if err != nil { 250 | return err 251 | } 252 | } 253 | 254 | return nil 255 | } 256 | 257 | func (r *DB) LastAcceptedDigest(pre string, seq int) ([]byte, error) { 258 | r.logLock.RLock() 259 | defer r.logLock.RUnlock() 260 | 261 | log, ok := r.logs[pre] 262 | if !ok { 263 | return nil, errors.New("not found") 264 | } 265 | 266 | last := log[len(log)-1] 267 | evt := last[len(last)-1] 268 | dig, err := evt.Event.GetDigest() 269 | if err != nil { 270 | return nil, err 271 | } 272 | 273 | return []byte(dig), nil 274 | } 275 | 276 | func (r *DB) EscrowOutOfOrderEvent(e *event.Message) error { 277 | return nil 278 | } 279 | 280 | func (r *DB) EscrowLikelyDuplicitiousEvent(e *event.Message) error { 281 | r.dupLock.Lock() 282 | defer r.dupLock.Unlock() 283 | 284 | pre := e.Event.Prefix 285 | l, ok := r.likelyDups[pre] 286 | if !ok { 287 | l := [][]*event.Message{{e}} 288 | r.likelyDups[pre] = l 289 | } else { 290 | sn := e.Event.SequenceInt() 291 | if sn < len(l) { 292 | l[sn] = append(l[sn], e) 293 | } else { 294 | r.likelyDups[pre] = append(l, []*event.Message{e}) 295 | } 296 | } 297 | 298 | return nil 299 | } 300 | 301 | func (r *DB) EscrowPendingEvent(e *event.Message) error { 302 | r.pendLock.Lock() 303 | defer r.pendLock.Unlock() 304 | 305 | pre := e.Event.Prefix 306 | l, ok := r.pending[pre] 307 | if !ok { 308 | l := [][]*event.Message{{e}} 309 | r.pending[pre] = l 310 | } else { 311 | sn := e.Event.SequenceInt() 312 | if sn < len(l) { 313 | l[sn] = append(l[sn], e) 314 | } else { 315 | r.pending[pre] = append(l, []*event.Message{e}) 316 | } 317 | } 318 | 319 | return nil 320 | } 321 | 322 | func (r *DB) RemovePendingEscrow(prefix string, sn int, dig string) error { 323 | r.pendLock.Lock() 324 | defer r.pendLock.Unlock() 325 | 326 | l, ok := r.pending[prefix] 327 | if ok { 328 | if sn < len(l) { 329 | digs := l[sn] 330 | n := 0 331 | for _, x := range digs { 332 | xdig, _ := x.Event.GetDigest() 333 | if xdig != dig { 334 | digs[n] = x 335 | n++ 336 | } 337 | } 338 | digs = digs[:n] 339 | } 340 | } 341 | 342 | return nil 343 | } 344 | 345 | func (r *DB) StreamPending(pre string, handler func(*event.Message) error) error { 346 | r.pendLock.RLock() 347 | defer r.pendLock.RUnlock() 348 | 349 | log, ok := r.pending[pre] 350 | if !ok { 351 | return errors.New("not found") 352 | } 353 | 354 | for _, evts := range log { 355 | for _, evt := range evts { 356 | err := handler(evt) 357 | if err != nil { 358 | return err 359 | } 360 | } 361 | } 362 | 363 | return nil 364 | 365 | } 366 | 367 | func (r *DB) LogTransferableReceipt(vrc *event.Receipt) error { 368 | r.rcptLock.Lock() 369 | defer r.rcptLock.Unlock() 370 | 371 | pre := vrc.Prefix 372 | quadlet := vrc.Text() 373 | 374 | _, ok := r.rcpts[pre] 375 | if !ok { 376 | r.rcpts[pre] = []string{} 377 | } 378 | 379 | r.rcpts[pre] = append(r.rcpts[pre], string(quadlet)) 380 | 381 | return nil 382 | } 383 | 384 | func (r *DB) LogNonTransferableReceipt(vrc *event.Receipt) error { 385 | r.rcptLock.Lock() 386 | defer r.rcptLock.Unlock() 387 | 388 | pre := vrc.Prefix 389 | couplet := vrc.Text() 390 | 391 | _, ok := r.rcpts[pre] 392 | if !ok { 393 | r.rcpts[pre] = []string{} 394 | } 395 | 396 | r.rcpts[pre] = append(r.rcpts[pre], string(couplet)) 397 | 398 | return nil 399 | } 400 | 401 | func (r *DB) StreamTransferableReceipts(pre string, sn int, handler func(quadlet []byte) error) error { 402 | r.rcptLock.Lock() 403 | defer r.rcptLock.Unlock() 404 | 405 | log, ok := r.rcpts[pre] 406 | if !ok { 407 | return nil 408 | } 409 | 410 | for _, quad := range log { 411 | err := handler([]byte(quad)) 412 | if err != nil { 413 | return err 414 | } 415 | } 416 | 417 | return nil 418 | } 419 | -------------------------------------------------------------------------------- /pkg/db/mem/mem_test.go: -------------------------------------------------------------------------------- 1 | package mem 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | 10 | "github.com/decentralized-identity/kerigo/pkg/event" 11 | ) 12 | 13 | func TestPut(t *testing.T) { 14 | 15 | t.Run("empty get", func(t *testing.T) { 16 | db := New() 17 | 18 | v, err := db.Get("test") 19 | assert.Empty(t, v) 20 | assert.NotNil(t, err) 21 | assert.Equal(t, "not found", err.Error()) 22 | }) 23 | 24 | t.Run("put and get", func(t *testing.T) { 25 | 26 | db := New() 27 | err := db.Put("test", []byte("value")) 28 | 29 | v, err := db.Get("test") 30 | 31 | assert.NoError(t, err) 32 | assert.Equal(t, []byte("value"), v) 33 | }) 34 | } 35 | 36 | func TestLogEvent(t *testing.T) { 37 | db := New() 38 | 39 | evt := &event.Event{ 40 | Prefix: "pre", 41 | Version: event.DefaultVersionString(event.JSON), 42 | EventType: "icp", 43 | Sequence: "0", 44 | Keys: []string{"k1.1", "k1.2", "k1.3"}, 45 | Next: "next1", 46 | Witnesses: []string{"w1"}, 47 | } 48 | 49 | msg := &event.Message{Event: evt} 50 | err := db.LogEvent(msg, true) 51 | assert.NoError(t, err) 52 | 53 | assert.Equal(t, db.LogSize("pre"), 1) 54 | icp, err := db.Inception("pre") 55 | assert.NoError(t, err) 56 | assert.EqualValues(t, evt, icp.Event) 57 | } 58 | 59 | func TestLogSize(t *testing.T) { 60 | db := New() 61 | 62 | evt := &event.Event{ 63 | Prefix: "pre", 64 | Version: event.DefaultVersionString(event.JSON), 65 | EventType: "icp", 66 | Sequence: "0", 67 | Keys: []string{"k1.1", "k1.2", "k1.3"}, 68 | Next: "next1", 69 | Witnesses: []string{"w1"}, 70 | } 71 | 72 | err := db.LogEvent(&event.Message{Event: evt}, true) 73 | assert.NoError(t, err) 74 | 75 | assert.Equal(t, db.LogSize("pre"), 1) 76 | evt = &event.Event{ 77 | Prefix: "pre", 78 | Version: event.DefaultVersionString(event.JSON), 79 | EventType: "ixn", 80 | Sequence: "1", 81 | } 82 | 83 | err = db.LogEvent(&event.Message{Event: evt}, true) 84 | require.NoError(t, err) 85 | 86 | assert.Equal(t, db.LogSize("pre"), 2) 87 | 88 | for i := 2; i < 12; i++ { 89 | evt.Sequence = fmt.Sprintf("%x", i) 90 | 91 | err = db.LogEvent(&event.Message{Event: evt}, true) 92 | require.NoError(t, err) 93 | } 94 | 95 | assert.Equal(t, db.LogSize("pre"), 12) 96 | 97 | } 98 | 99 | func TestStreamLog(t *testing.T) { 100 | db := New() 101 | 102 | evt := &event.Event{ 103 | Prefix: "pre", 104 | Version: event.DefaultVersionString(event.JSON), 105 | EventType: "icp", 106 | Sequence: "0", 107 | Keys: []string{"k1.1", "k1.2", "k1.3"}, 108 | Next: "next1", 109 | Witnesses: []string{"w1"}, 110 | } 111 | 112 | err := db.LogEvent(&event.Message{Event: evt}, true) 113 | assert.NoError(t, err) 114 | 115 | assert.Equal(t, db.LogSize("pre"), 1) 116 | evt = &event.Event{ 117 | Prefix: "pre", 118 | Version: event.DefaultVersionString(event.JSON), 119 | EventType: "ixn", 120 | Sequence: "1", 121 | } 122 | 123 | err = db.LogEvent(&event.Message{Event: evt}, true) 124 | require.NoError(t, err) 125 | 126 | assert.Equal(t, db.LogSize("pre"), 2) 127 | 128 | for i := 2; i < 12; i++ { 129 | evt.Sequence = fmt.Sprintf("%x", i) 130 | 131 | err = db.LogEvent(&event.Message{Event: evt}, true) 132 | require.NoError(t, err) 133 | } 134 | 135 | count := db.LogSize("pre") 136 | 137 | assert.NoError(t, err) 138 | assert.Equal(t, 12, count) 139 | } 140 | 141 | func TestStreamEstablisment(t *testing.T) { 142 | db := New() 143 | 144 | evt := &event.Event{ 145 | Prefix: "pre", 146 | Version: event.DefaultVersionString(event.JSON), 147 | EventType: "icp", 148 | Sequence: "0", 149 | Keys: []string{"k1.1", "k1.2", "k1.3"}, 150 | Next: "next1", 151 | Witnesses: []string{"w1"}, 152 | } 153 | 154 | err := db.LogEvent(&event.Message{Event: evt}, true) 155 | assert.NoError(t, err) 156 | 157 | assert.Equal(t, db.LogSize("pre"), 1) 158 | evt = &event.Event{ 159 | Prefix: "pre", 160 | Version: event.DefaultVersionString(event.JSON), 161 | EventType: "ixn", 162 | Sequence: "1", 163 | } 164 | 165 | err = db.LogEvent(&event.Message{Event: evt}, true) 166 | require.NoError(t, err) 167 | 168 | assert.Equal(t, db.LogSize("pre"), 2) 169 | 170 | for i := 2; i < 4; i++ { 171 | evt.Sequence = fmt.Sprintf("%x", i) 172 | 173 | err = db.LogEvent(&event.Message{Event: evt}, true) 174 | require.NoError(t, err) 175 | } 176 | 177 | evt = &event.Event{ 178 | Prefix: "pre", 179 | Version: event.DefaultVersionString(event.JSON), 180 | EventType: "rot", 181 | Sequence: "4", 182 | Keys: []string{"k2.1", "k2.2", "k2.3"}, 183 | Next: "next2", 184 | Witnesses: []string{"w1"}, 185 | } 186 | err = db.LogEvent(&event.Message{Event: evt}, true) 187 | require.NoError(t, err) 188 | 189 | count := 0 190 | err = db.StreamEstablisment("pre", func(msg *event.Message) error { 191 | count++ 192 | return nil 193 | }) 194 | 195 | assert.NoError(t, err) 196 | assert.Equal(t, 2, count) 197 | 198 | } 199 | 200 | func TestSeen(t *testing.T) { 201 | db := New() 202 | 203 | evt := &event.Event{ 204 | Prefix: "pre", 205 | Version: event.DefaultVersionString(event.JSON), 206 | EventType: "icp", 207 | Sequence: "0", 208 | Keys: []string{"k1.1", "k1.2", "k1.3"}, 209 | Next: "next1", 210 | Witnesses: []string{"w1"}, 211 | } 212 | 213 | err := db.LogEvent(&event.Message{Event: evt}, true) 214 | assert.NoError(t, err) 215 | 216 | ok := db.Seen("pre") 217 | assert.True(t, ok) 218 | 219 | ok = db.Seen("not-pre") 220 | assert.False(t, ok) 221 | } 222 | 223 | func TestInception(t *testing.T) { 224 | db := New() 225 | 226 | orig := &event.Event{ 227 | Prefix: "pre", 228 | Version: event.DefaultVersionString(event.JSON), 229 | EventType: "icp", 230 | Sequence: "0", 231 | Keys: []string{"k1.1", "k1.2", "k1.3"}, 232 | Next: "next1", 233 | Witnesses: []string{"w1"}, 234 | } 235 | 236 | err := db.LogEvent(&event.Message{Event: orig}, true) 237 | assert.NoError(t, err) 238 | 239 | assert.Equal(t, db.LogSize("pre"), 1) 240 | evt := &event.Event{ 241 | Prefix: "pre", 242 | Version: event.DefaultVersionString(event.JSON), 243 | EventType: "ixn", 244 | Sequence: "1", 245 | } 246 | 247 | err = db.LogEvent(&event.Message{Event: evt}, true) 248 | require.NoError(t, err) 249 | 250 | evt = &event.Event{ 251 | Prefix: "pre", 252 | Version: event.DefaultVersionString(event.JSON), 253 | EventType: "rot", 254 | Sequence: "2", 255 | Keys: []string{"k2.1", "k2.2", "k2.3"}, 256 | Next: "next2", 257 | Witnesses: []string{"w1"}, 258 | } 259 | err = db.LogEvent(&event.Message{Event: evt}, true) 260 | require.NoError(t, err) 261 | 262 | icp, err := db.Inception("pre") 263 | assert.NoError(t, err) 264 | assert.Equal(t, orig, icp.Event) 265 | } 266 | 267 | func TestCurrentEvent(t *testing.T) { 268 | db := New() 269 | 270 | orig := &event.Event{ 271 | Prefix: "pre", 272 | Version: event.DefaultVersionString(event.JSON), 273 | EventType: "icp", 274 | Sequence: "0", 275 | Keys: []string{"k1.1", "k1.2", "k1.3"}, 276 | Next: "next1", 277 | Witnesses: []string{"w1"}, 278 | } 279 | 280 | err := db.LogEvent(&event.Message{Event: orig}, true) 281 | assert.NoError(t, err) 282 | 283 | assert.Equal(t, db.LogSize("pre"), 1) 284 | rot := &event.Event{ 285 | Prefix: "pre", 286 | Version: event.DefaultVersionString(event.JSON), 287 | EventType: "rot", 288 | Sequence: "1", 289 | Keys: []string{"k2.1", "k2.2", "k2.3"}, 290 | Next: "next2", 291 | Witnesses: []string{"w1"}, 292 | } 293 | 294 | err = db.LogEvent(&event.Message{Event: rot}, true) 295 | require.NoError(t, err) 296 | 297 | evt := &event.Event{ 298 | Prefix: "pre", 299 | Version: event.DefaultVersionString(event.JSON), 300 | EventType: "ixn", 301 | Sequence: "2", 302 | } 303 | err = db.LogEvent(&event.Message{Event: evt}, true) 304 | require.NoError(t, err) 305 | 306 | cur, err := db.CurrentEvent("pre") 307 | assert.NoError(t, err) 308 | assert.Equal(t, evt, cur.Event) 309 | } 310 | 311 | func TestCurrentEstablishmentEvent(t *testing.T) { 312 | db := New() 313 | 314 | orig := &event.Event{ 315 | Prefix: "pre", 316 | Version: event.DefaultVersionString(event.JSON), 317 | EventType: "icp", 318 | Sequence: "0", 319 | Keys: []string{"k1.1", "k1.2", "k1.3"}, 320 | Next: "next1", 321 | Witnesses: []string{"w1"}, 322 | } 323 | 324 | err := db.LogEvent(&event.Message{Event: orig}, true) 325 | assert.NoError(t, err) 326 | assert.Equal(t, db.LogSize("pre"), 1) 327 | 328 | rot := &event.Event{ 329 | Prefix: "pre", 330 | Version: event.DefaultVersionString(event.JSON), 331 | EventType: "rot", 332 | Sequence: "1", 333 | Keys: []string{"k2.1", "k2.2", "k2.3"}, 334 | Next: "next2", 335 | Witnesses: []string{"w1"}, 336 | } 337 | 338 | err = db.LogEvent(&event.Message{Event: rot}, true) 339 | require.NoError(t, err) 340 | 341 | evt := &event.Event{ 342 | Prefix: "pre", 343 | Version: event.DefaultVersionString(event.JSON), 344 | EventType: "ixn", 345 | Sequence: "2", 346 | } 347 | err = db.LogEvent(&event.Message{Event: evt}, true) 348 | require.NoError(t, err) 349 | 350 | cur, err := db.CurrentEstablishmentEvent("pre") 351 | assert.NoError(t, err) 352 | assert.Equal(t, rot, cur.Event) 353 | } 354 | 355 | func TestEventAt(t *testing.T) { 356 | db := New() 357 | 358 | icp := &event.Event{ 359 | Prefix: "pre", 360 | Version: event.DefaultVersionString(event.JSON), 361 | EventType: "icp", 362 | Sequence: "0", 363 | Keys: []string{"k1.1", "k1.2", "k1.3"}, 364 | Next: "next1", 365 | Witnesses: []string{"w1"}, 366 | } 367 | 368 | err := db.LogEvent(&event.Message{Event: icp}, true) 369 | assert.NoError(t, err) 370 | 371 | rot := &event.Event{ 372 | Prefix: "pre", 373 | Version: event.DefaultVersionString(event.JSON), 374 | EventType: "rot", 375 | Sequence: "1", 376 | Keys: []string{"k2.1", "k2.2", "k2.3"}, 377 | Next: "next2", 378 | Witnesses: []string{"w1"}, 379 | } 380 | 381 | err = db.LogEvent(&event.Message{Event: rot}, true) 382 | require.NoError(t, err) 383 | 384 | ixn := &event.Event{ 385 | Prefix: "pre", 386 | Version: event.DefaultVersionString(event.JSON), 387 | EventType: "ixn", 388 | Sequence: "2", 389 | } 390 | 391 | err = db.LogEvent(&event.Message{Event: ixn}, true) 392 | require.NoError(t, err) 393 | 394 | at, err := db.EventAt("pre", 0) 395 | assert.NoError(t, err) 396 | assert.Equal(t, icp, at.Event) 397 | 398 | at, err = db.EventAt("pre", 1) 399 | assert.NoError(t, err) 400 | assert.Equal(t, rot, at.Event) 401 | 402 | at, err = db.EventAt("pre", 2) 403 | assert.NoError(t, err) 404 | assert.Equal(t, ixn, at.Event) 405 | 406 | } 407 | --------------------------------------------------------------------------------