27
vendor/github.com/anacrolix/dht/v2/bep44/error.go
generated
vendored
Normal file
27
vendor/github.com/anacrolix/dht/v2/bep44/error.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package bep44
|
||||
|
||||
import "github.com/anacrolix/dht/v2/krpc"
|
||||
|
||||
var (
|
||||
ErrValueFieldTooBig = krpc.Error{
|
||||
Code: krpc.ErrorCodeMessageValueFieldTooBig,
|
||||
Msg: "message (v field) too big",
|
||||
}
|
||||
ErrInvalidSignature = krpc.Error{
|
||||
Code: krpc.ErrorCodeInvalidSignature,
|
||||
Msg: "invalid signature",
|
||||
}
|
||||
|
||||
ErrSaltFieldTooBig = krpc.Error{
|
||||
Code: krpc.ErrorCodeSaltFieldTooBig,
|
||||
Msg: "salt (salt field) too big",
|
||||
}
|
||||
ErrCasHashMismatched = krpc.Error{
|
||||
Code: krpc.ErrorCodeCasHashMismatched,
|
||||
Msg: "the CAS hash mismatched, re-read value and try again",
|
||||
}
|
||||
ErrSequenceNumberLessThanCurrent = krpc.Error{
|
||||
Code: krpc.ErrorCodeSequenceNumberLessThanCurrent,
|
||||
Msg: "sequence number less than current",
|
||||
}
|
||||
)
|
||||
177
vendor/github.com/anacrolix/dht/v2/bep44/item.go
generated
vendored
Normal file
177
vendor/github.com/anacrolix/dht/v2/bep44/item.go
generated
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
package bep44
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ed25519"
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/anacrolix/torrent/bencode"
|
||||
)
|
||||
|
||||
var Empty32ByteArray [32]byte
|
||||
|
||||
type Item struct {
|
||||
// time when this object was added to storage
|
||||
created time.Time
|
||||
|
||||
// Value to be stored
|
||||
V interface{}
|
||||
|
||||
// 32 byte ed25519 public key
|
||||
K [32]byte
|
||||
Salt []byte
|
||||
Sig [64]byte
|
||||
Cas int64
|
||||
Seq int64
|
||||
}
|
||||
|
||||
func (i *Item) ToPut() Put {
|
||||
p := Put{
|
||||
V: i.V,
|
||||
Salt: i.Salt,
|
||||
Sig: i.Sig,
|
||||
Cas: i.Cas,
|
||||
Seq: i.Seq,
|
||||
}
|
||||
if i.K != Empty32ByteArray {
|
||||
p.K = &i.K
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// NewItem creates a new arbitrary DHT element. The distinction between storing mutable
|
||||
// and immutable items is the inclusion of a public key, a sequence number, signature
|
||||
// and an optional salt.
|
||||
//
|
||||
// cas parameter is short for compare and swap, it has similar semantics as CAS CPU
|
||||
// instructions. It is used to avoid race conditions when multiple nodes are writing
|
||||
// to the same slot in the DHT. It is optional. If present it specifies the sequence
|
||||
// number of the data blob being overwritten by the put.
|
||||
//
|
||||
// salt parameter is used to make possible several targets using the same private key.
|
||||
//
|
||||
// The optional seq field specifies that an item's value should only be sent if its
|
||||
// sequence number is greater than the given value.
|
||||
func NewItem(value interface{}, salt []byte, seq, cas int64, k ed25519.PrivateKey) (*Item, error) {
|
||||
v, err := bencode.Marshal(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var kk [32]byte
|
||||
var sig [64]byte
|
||||
|
||||
if k != nil {
|
||||
pk := []byte(k.Public().(ed25519.PublicKey))
|
||||
copy(kk[:], pk)
|
||||
copy(sig[:], Sign(k, salt, seq, v))
|
||||
}
|
||||
|
||||
return &Item{
|
||||
V: value,
|
||||
Salt: salt,
|
||||
Cas: cas,
|
||||
Seq: seq,
|
||||
|
||||
K: kk,
|
||||
Sig: sig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (i *Item) Target() Target {
|
||||
if i.IsMutable() {
|
||||
return sha1.Sum(append(i.K[:], i.Salt...))
|
||||
}
|
||||
|
||||
return sha1.Sum(bencode.MustMarshal(i.V))
|
||||
}
|
||||
|
||||
func (i *Item) Modify(value interface{}, k ed25519.PrivateKey) bool {
|
||||
if !i.IsMutable() {
|
||||
return false
|
||||
}
|
||||
|
||||
v, err := bencode.Marshal(value)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
i.V = value
|
||||
i.Seq++
|
||||
var sig [64]byte
|
||||
copy(sig[:], Sign(k, i.Salt, i.Seq, v))
|
||||
i.Sig = sig
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Item) IsMutable() bool {
|
||||
return s.K != Empty32ByteArray
|
||||
}
|
||||
|
||||
func bufferToSign(salt, bv []byte, seq int64) []byte {
|
||||
var bts []byte
|
||||
if len(salt) != 0 {
|
||||
bts = append(bts, []byte("4:salt")...)
|
||||
x := bencode.MustMarshal(salt)
|
||||
bts = append(bts, x...)
|
||||
}
|
||||
bts = append(bts, []byte(fmt.Sprintf("3:seqi%de1:v", seq))...)
|
||||
bts = append(bts, bv...)
|
||||
return bts
|
||||
}
|
||||
|
||||
func Check(i *Item) error {
|
||||
bv, err := bencode.Marshal(i.V)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(bv) > 1000 {
|
||||
return ErrValueFieldTooBig
|
||||
}
|
||||
|
||||
if !i.IsMutable() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(i.Salt) > 64 {
|
||||
return ErrSaltFieldTooBig
|
||||
}
|
||||
|
||||
if !Verify(i.K[:], i.Salt, i.Seq, bv, i.Sig[:]) {
|
||||
return ErrInvalidSignature
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CheckIncoming(stored, incoming *Item) error {
|
||||
// If the sequence number is equal, and the value is also the same,
|
||||
// the node SHOULD reset its timeout counter.
|
||||
if stored.Seq == incoming.Seq {
|
||||
if bytes.Equal(
|
||||
bencode.MustMarshal(stored.V),
|
||||
bencode.MustMarshal(incoming.V),
|
||||
) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if stored.Seq >= incoming.Seq {
|
||||
return ErrSequenceNumberLessThanCurrent
|
||||
}
|
||||
|
||||
// Cas should be ignored if not present
|
||||
if stored.Cas == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if stored.Cas != incoming.Cas {
|
||||
return ErrCasHashMismatched
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
13
vendor/github.com/anacrolix/dht/v2/bep44/key.go
generated
vendored
Normal file
13
vendor/github.com/anacrolix/dht/v2/bep44/key.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package bep44
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
)
|
||||
|
||||
func Sign(k ed25519.PrivateKey, salt []byte, seq int64, bv []byte) []byte {
|
||||
return ed25519.Sign(k, bufferToSign(salt, bv, seq))
|
||||
}
|
||||
|
||||
func Verify(k ed25519.PublicKey, salt []byte, seq int64, bv []byte, sig []byte) bool {
|
||||
return ed25519.Verify(k, bufferToSign(salt, bv, seq), sig)
|
||||
}
|
||||
49
vendor/github.com/anacrolix/dht/v2/bep44/memory.go
generated
vendored
Normal file
49
vendor/github.com/anacrolix/dht/v2/bep44/memory.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
package bep44
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var _ Store = &Memory{}
|
||||
|
||||
type Memory struct {
|
||||
// protects m
|
||||
mu sync.RWMutex
|
||||
m map[Target]*Item
|
||||
}
|
||||
|
||||
func NewMemory() *Memory {
|
||||
return &Memory{
|
||||
m: make(map[Target]*Item),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Memory) Put(i *Item) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
m.m[i.Target()] = i
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Memory) Get(t Target) (*Item, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
i, ok := m.m[t]
|
||||
if !ok {
|
||||
return nil, ErrItemNotFound
|
||||
}
|
||||
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *Memory) Del(t Target) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
delete(m.m, t)
|
||||
|
||||
return nil
|
||||
}
|
||||
47
vendor/github.com/anacrolix/dht/v2/bep44/put.go
generated
vendored
Normal file
47
vendor/github.com/anacrolix/dht/v2/bep44/put.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
package bep44
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/sha1"
|
||||
|
||||
"github.com/anacrolix/torrent/bencode"
|
||||
)
|
||||
|
||||
type Put struct {
|
||||
V interface{}
|
||||
K *[32]byte
|
||||
Salt []byte
|
||||
Sig [64]byte
|
||||
Cas int64
|
||||
Seq int64
|
||||
}
|
||||
|
||||
func (p *Put) ToItem() *Item {
|
||||
i := &Item{
|
||||
V: p.V,
|
||||
Salt: p.Salt,
|
||||
Sig: p.Sig,
|
||||
Cas: p.Cas,
|
||||
Seq: p.Seq,
|
||||
}
|
||||
if p.K != nil {
|
||||
i.K = *p.K
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func (p *Put) Sign(k ed25519.PrivateKey) {
|
||||
copy(p.Sig[:], Sign(k, p.Salt, p.Seq, bencode.MustMarshal(p.V)))
|
||||
}
|
||||
|
||||
func (i *Put) Target() Target {
|
||||
if i.IsMutable() {
|
||||
return MakeMutableTarget(*i.K, i.Salt)
|
||||
} else {
|
||||
return sha1.Sum(bencode.MustMarshal(i.V))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Put) IsMutable() bool {
|
||||
return s.K != nil
|
||||
}
|
||||
65
vendor/github.com/anacrolix/dht/v2/bep44/store.go
generated
vendored
Normal file
65
vendor/github.com/anacrolix/dht/v2/bep44/store.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
package bep44
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ErrItemNotFound = errors.New("item not found")
|
||||
|
||||
type Store interface {
|
||||
Put(*Item) error
|
||||
Get(Target) (*Item, error)
|
||||
Del(Target) error
|
||||
}
|
||||
|
||||
// Wrapper is in charge of validate all new items and
|
||||
// decide when to store, or ignore them depending of the BEP 44 definition.
|
||||
// It is also in charge of removing expired items.
|
||||
type Wrapper struct {
|
||||
s Store
|
||||
exp time.Duration
|
||||
}
|
||||
|
||||
func NewWrapper(s Store, exp time.Duration) *Wrapper {
|
||||
return &Wrapper{s: s, exp: exp}
|
||||
}
|
||||
|
||||
func (w *Wrapper) Put(i *Item) error {
|
||||
if err := Check(i); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
is, err := w.s.Get(i.Target())
|
||||
if errors.Is(err, ErrItemNotFound) {
|
||||
i.created = time.Now().Local()
|
||||
return w.s.Put(i)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := CheckIncoming(is, i); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i.created = time.Now().Local()
|
||||
return w.s.Put(i)
|
||||
}
|
||||
|
||||
func (w *Wrapper) Get(t Target) (*Item, error) {
|
||||
i, err := w.s.Get(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if i.created.Add(w.exp).After(time.Now().Local()) {
|
||||
return i, nil
|
||||
}
|
||||
|
||||
if err := w.s.Del(t); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, ErrItemNotFound
|
||||
}
|
||||
11
vendor/github.com/anacrolix/dht/v2/bep44/target.go
generated
vendored
Normal file
11
vendor/github.com/anacrolix/dht/v2/bep44/target.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
package bep44
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
)
|
||||
|
||||
type Target = [sha1.Size]byte
|
||||
|
||||
func MakeMutableTarget(pubKey [32]byte, salt []byte) Target {
|
||||
return sha1.Sum(append(pubKey[:], salt...))
|
||||
}
|
||||
Reference in New Issue
Block a user