passtor

package module
v0.0.0-...-8ef1ed1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 10, 2021 License: GPL-3.0 Imports: 22 Imported by: 0

README

Passtör (Decentralized Password Manager)

Authors: Cedric Maire, Guillaume Michel, Xavier Pantet

Project design

Our systems goal is to provide a resilient and reliable password manager as simple as possible. This is achieved by building a distributed and decentralized system powered by a Distributed Hash Table (DHT), ensuring high avaiability and resilience.

Users only have to remember their username and master password. We assume that the master password will never be lost and is strong enough to resist traditional brute-force or dictionary attacks. The client's symmetric key derived from the master password, using Argon2, a Key Derivative Function (KDF). The user's passwords are encrypted with ChaCha20, using the generated key, and stored in the DHT. The user can fetch the passwords at anytime from any device, and decrypt the passwords. The encrypted passwords are available to anyone to download, but only the user with the knowledge of the master secret will be able to decrypt it.

Passtör uses a DHT taking inspiration from the famous Kademlia DHT [1], and allows replication of encrypted data with a replication factor specific for each user account. The data associated with each user account is distributed in the DHT, thus each node only needs to store ( #accounts * repl / #nodes ) accounts on average, repl being the constant average replication factor. Thus the required storage capacity is linear in the number of registered accounts per node which scales well with a large number of nodes.

Our DHT follows Kademlia design with XOR distance, and its buckets follow a least-recently seen eviction policy, except that nodes answering to periodic ping requests are never removed from the list. Our system provides strong consistency. Each update overrides the previous version of the data, thus there is no stale data in the system. To join the DHT, a new node only needs to know the address of at least another peer already in the DHT.

The full specifications can be found here.

alt text

Files

This folder contains the client implementation. To launch the client, just type:

go build
./client

This folder contains the passtor (dht node) implementation. The server should be used as follow:

Usage of ./server:
  -addr string
    	address used to communicate other passtors instances (default "127.0.0.1:5000")
  -name string
    	name of the Passtor instance
  -peers string
    	bootstrap peer addresses
  -v int
    	verbose mode (default 1)

To test our program it is recommended to run at least 5 passtor instances.

Contains scripts that automatically launch multiple instances of passtors.

Contains a few tests we created for the project.

  • System constants

All system constants are defined in const.go.

How to run this project

To run this project you can run the following code:

git clone https://github.com/guillaumemichel/passtor
cd passtor
go build

Then you need to launch multiple passtor instances to start the system. Once it is done, you can connect to any passtor instance with the client, and create an account, store, modify and download your credentials.

References

[1] P. Maymounkov and D. Mazieres, “Kademlia: A peer-to-peer information system based on the xor metric,” Peer-to-Peer Systems, pp. 53–65, 2002.

Documentation

Index

Constants

View Source
const (
	// DHTK K parameter for DHT K-buckets
	DHTK = 5
	// ALPHA DHT concurrency parameter
	ALPHA = 2
	// REPL replication factor
	REPL = 3
	// NREQ minimal number of response after Fetch
	NREQ = 2
	// THRESHOLD of answers before returning
	THRESHOLD = 0.333
	// TIMEOUT value when waiting for an answer
	TIMEOUT = 1 * time.Second
	// MINRETRIES min number of attemps before giving up reaching an host
	MINRETRIES = 1
	// MAXRETRIES max number of attemps before giving up reaching an host
	MAXRETRIES = 4
	// BUFFERSIZE size of the udp connection read buffer
	BUFFERSIZE = 8192
	// BYTELENGTH number of bits in a byte
	BYTELENGTH uint16 = 8

	// PASSPHRASELENGHT default length in words for a passphrase
	PASSPHRASELENGHT = 8
	// PASSPHRASESEP default word seperator in a passphrase
	PASSPHRASESEP = "."

	// V0 verbose level 0 (no output)
	V0 = 0
	// V1 verbose level 1 (normal output)
	V1 = 1
	// V2 verbose level 2 (mode verbose)
	V2 = 2
	// V3 verbose level 3 (mode verbose++)
	V3 = 3
	// TCPMAXPACKETSIZE is the largest size in bytes of a TCP packet
	TCPMAXPACKETSIZE = 65535
	// REPUBLISHINTERVAL average time interval between republish in minutes
	REPUBLISHINTERVAL = 5
)
View Source
const (
	// SALTLENGTH is the length of the salt to be used by Argon2 as KDF
	SALTLENGTH = 16

	// ARGONITERATIONS is the number of iterations to be used in the Argon2 algo
	ARGONITERATIONS = 3

	// ARGONMEMORY is the size in bytes to be used by Argon2 in memory
	ARGONMEMORY = 64 * 1024

	// ARGONPARALELLISM is the number of cores to be used by Argon2
	ARGONPARALELLISM = 2

	// SECRETLENGTH is the length of the key produced by Argon2
	SECRETLENGTH = SYMMKEYSIZE
)
View Source
const HASHSIZE = 64

HASHSIZE size of a hash in byte

View Source
const (
	// NONCESIZE size in bytes for a nonce
	NONCESIZE = chacha20poly1305.NonceSizeX
)
View Source
const (
	// SIGNATURESIZE size in bytes for a signature
	SIGNATURESIZE = ed25519.SignatureSize
)
View Source
const (
	// SYMMKEYSIZE size in bytes for a symmetric key
	SYMMKEYSIZE = chacha20poly1305.KeySize
)

Variables

View Source
var ALREADYSTORED = "Account already stored and up-to-date"

ALREADYSTORED error string

View Source
var NOERROR = ""

NOERROR string

Functions

func Decrypt

func Decrypt(ciphertext []byte, nonce Nonce, key SymmetricKey) ([]byte, error)

Decrypt decrypts the given ciphertext under the given key

func DuplicateMap

func DuplicateMap(data map[Hash]Login) map[Hash]Login

func Encrypt

func Encrypt(data []byte, key SymmetricKey) (EncryptedData, Nonce, error)

Encrypt encrypts the given data under the given key using ChaCha20 stream cipher

func Generate

func Generate() (PublicKey, PrivateKey, SymmetricKey, error)

func HashToBytes

func HashToBytes(h Hash) []byte

func NewSecret

func NewSecret(userID, masterPassword string) (Secret, Salt, error)

Secret generates a secret for the given user

func NonceToBytes

func NonceToBytes(nonce Nonce) []byte

func ParsePeers

func ParsePeers(peerList string) []net.UDPAddr

ParsePeers parse peer list in string format to udp addresses

func Passphrase

func Passphrase() (string, error)

func PrintStatuses

func PrintStatuses(h Hash, statuses []*LookupStatus)

PrintStatuses print given lookup statuses

func RandInt

func RandInt(n int64) int64

RandInt generate a random int64 between 0 and given n

func RandomBytes

func RandomBytes(size uint) ([]byte, error)

RandomBytes generates an array of random bytes of the given size

func SaltToBytes

func SaltToBytes(salt Salt) []byte

func SignatureToBytes

func SignatureToBytes(signature Signature) []byte

func SymmetricKeyToBytes

func SymmetricKeyToBytes(symmK SymmetricKey) []byte

SymmetricKeyToBytes converts a symmetric key to a raw array of bytes

func Timeout

func Timeout(timeout time.Duration) *chan bool

Timeout creates a clock that writes to the returned channel after the time value given as argument

func Verify

func Verify(data []byte, signature Signature, key PublicKey) bool

Verify checks that the signature and data are valid under the given public key

Types

type Account

type Account struct {
	ID        Hash
	Keys      Keys
	Version   uint32
	Data      map[Hash]Login
	MetaData  AccountMetaData
	Signature Signature
}

Account groups everything that has been stored by a single user.

func MostRepresented

func MostRepresented(accounts []Account, min int) (*Account, bool)

MostRepresented returns the most represented verified (in the sense of signature equality)

func (Account) AddLogin

func (account Account) AddLogin(loginClient LoginClient, loginPassword string, keysClient KeysClient) (Account, error)

func (Account) DeleteLogin

func (account Account) DeleteLogin(ID Hash, sk PrivateKey) (Account, error)

func (Account) GetLoginClientList

func (account Account) GetLoginClientList(symmK SymmetricKey) ([]LoginClient, error)

func (Account) GetLoginPassword

func (account Account) GetLoginPassword(loginClient LoginClient, symmK SymmetricKey) ([]byte, error)

func (Account) GetSignData

func (account Account) GetSignData() []byte

func (Account) Sign

func (account Account) Sign(sk PrivateKey) Account

func (Account) ToAccountClient

func (account Account) ToAccountClient(ID string, secret Secret) (AccountClient, error)

func (Account) ToAccountNetwork

func (account Account) ToAccountNetwork() AccountNetwork

func (Account) UpdateLoginPassword

func (account Account) UpdateLoginPassword(ID Hash, loginPassword string, keysClient KeysClient) (Account, error)

func (Account) Verify

func (account Account) Verify() bool

type AccountClient

type AccountClient struct {
	ID   string
	Keys KeysClient
}

Account used only client side to store info about the current user.

func (AccountClient) GetID

func (accountClient AccountClient) GetID() Hash

func (AccountClient) ToEmptyAccount

func (accountClient AccountClient) ToEmptyAccount(secret Secret, secretSalt Salt) (Account, error)

type AccountInfo

type AccountInfo struct {
	Account Account
	Repl    uint32
	Mutex   *sync.Mutex
}

type AccountMessage

type AccountMessage struct {
	Account AccountNetwork
	Repl    uint32
}

AccountMessage message requesting a node to allocate a file or fetching an account info

type AccountMetaData

type AccountMetaData struct {
	SecretSalt          Salt
	PrivateKeySeedNonce Nonce
	SymmetricKeyNonce   Nonce
}

AccountMetaData for the Account structure.

func (AccountMetaData) Hash

func (accountMetaData AccountMetaData) Hash() Hash

type AccountNetwork

type AccountNetwork struct {
	ID        Hash
	Keys      Keys
	Version   uint32
	Data      []Login
	MetaData  AccountMetaData
	Signature Signature
}

AccountNetwork used to be able to encode to be sent over the network

func (AccountNetwork) ToAccount

func (accountNetwork AccountNetwork) ToAccount() Account

type Accounts

type Accounts map[Hash]*AccountInfo

Accounts is the collection of all created accounts.

type Bucket

type Bucket struct {
	Mutex *sync.Mutex
	Head  *BucketElement
	Tail  *BucketElement
	Size  uint
}

Bucket structure representing Kademlia k-buckets

func (*Bucket) Find

func (b *Bucket) Find(nodeAddr *NodeAddr) *BucketElement

Find and return the element corresponding to the given address, returns nil if not found

func (*Bucket) GetList

func (b *Bucket) GetList() []NodeAddr

GetList returns the list of node addresses in the given bucket

func (*Bucket) Insert

func (b *Bucket) Insert(nodeAddr *NodeAddr)

Insert a new NodeAddress in the Bucket, called only if bucket not full

func (*Bucket) MoveToHead

func (b *Bucket) MoveToHead(el *BucketElement)

MoveToHead moves an element of the list to the head

func (*Bucket) Replace

func (b *Bucket) Replace(old *BucketElement, new *NodeAddr)

Replace the a node address in the list by a new one

type BucketElement

type BucketElement struct {
	NodeAddr *NodeAddr
	Next     *BucketElement
	Prev     *BucketElement
}

BucketElement represent individual elements of the k-buckets

type Client

type Client struct {
	App           *tview.Application
	Node          string
	AccountClient AccountClient
	Account       Account
}

type ClientMessage

type ClientMessage struct {
	Push *AccountNetwork
	Pull *Hash
}

ClientMessage represents a message than can be sent from a client to a node

type Credentials

type Credentials struct {
	Username EncryptedData
	Password EncryptedData
}

Credentials for a given service.

func (Credentials) Hash

func (credentials Credentials) Hash() Hash

type EncryptedData

type EncryptedData []byte

EncryptedData generic format

type Hash

type Hash [HASHSIZE]byte

Hash is a flexible type to handle hashes

var MAXDISTANCE Hash

MAXDISTANCE maximum distance between two hashes

func BytesToHash

func BytesToHash(array []byte) Hash

func GetKeysSorted

func GetKeysSorted(data map[Hash]Login) []Hash

func H

func H(data []byte) Hash

H hashes the given bytes value

func HashLogins

func HashLogins(logins map[Hash]Login) Hash

func (Hash) Compare

func (hash0 Hash) Compare(hash1 Hash) int

Compare two hashes, returns 1 if first hash smaller than the second, -1 if the second is smaller than the first, and 0 if they are equal

func (Hash) Hex

func (hash0 Hash) Hex() string

Hex representation of Hash

func (Hash) PrintDistancesToHash

func (hash0 Hash) PrintDistancesToHash(list []NodeAddr)

PrintDistancesToHash print the distance from a list of node addresses to a hash

func (Hash) String

func (hash0 Hash) String() string

String base64 representation of Hash

func (Hash) XOR

func (hash0 Hash) XOR(hash1 Hash) Hash

XOR function computing the XOR distance between two hashes

type Keys

type Keys struct {
	PublicKey      PublicKey
	PrivateKeySeed EncryptedData
	SymmetricKey   EncryptedData
}

Keys used to encrypt, or sign data.

func (Keys) Hash

func (keys Keys) Hash() Hash

func (Keys) ToKeysClient

func (keys Keys) ToKeysClient(secret Secret,
	privateKeySeedNonce Nonce,
	symmetricKeyNonce Nonce) (KeysClient, error)

type KeysClient

type KeysClient struct {
	PublicKey    PublicKey
	PrivateKey   PrivateKey
	SymmetricKey SymmetricKey
}

KeysClient used only client side to store the keys used to sign or en/de-crypt data.

func (KeysClient) Hash

func (keysClient KeysClient) Hash() Hash

func (KeysClient) ToKeys

func (keysClient KeysClient) ToKeys(secret Secret) (Keys, Nonce, Nonce, error)

type Login

type Login struct {
	ID          Hash
	Service     EncryptedData
	Credentials Credentials
	MetaData    LoginMetaData
}

Login is a tuple of credentials and corresponding metadata to ensure validity.

func (Login) Hash

func (login Login) Hash() Hash

func (Login) ToLoginClient

func (login Login) ToLoginClient(symmK SymmetricKey) (LoginClient, error)

type LoginClient

type LoginClient struct {
	Service  string
	Username string
}

LoginClient used to display restricted plaintext info about a login

func (LoginClient) GetID

func (loginClient LoginClient) GetID(symmK SymmetricKey) Hash

func (LoginClient) ToNewLogin

func (loginClient LoginClient) ToNewLogin(keysClient KeysClient, loginPassword string) (Login, error)

type LoginMetaData

type LoginMetaData struct {
	ServiceNonce  Nonce
	UsernameNonce Nonce
	PasswordNonce Nonce
}

LoginMetaData for the Login structure.

func (LoginMetaData) Hash

func (logInMetaData LoginMetaData) Hash() Hash

type LookupStatus

type LookupStatus struct {
	NodeAddr NodeAddr
	Tested   bool
	Failed   bool
}

LookupStatus type used by the lookup RPC

func NewLookupStatus

func NewLookupStatus(nodeAddr NodeAddr) *LookupStatus

NewLookupStatus returns new lookup status structure for given nodeaddr

type Message

type Message struct {
	ID            uint64      // message ID
	Reply         bool        // message is a reply
	Sender        *NodeAddr   // sender identity
	Ping          *bool       // non nil if message is a ping message
	LookupReq     *Hash       // value to lookup
	LookupRep     *[]NodeAddr // lookup response
	AllocationReq *AccountMessage
	AllocationRep *string
	FetchReq      *Hash
	FetchRep      *AccountMessage
}

Message structure defining messages exchanged between passtors

type MessageCounter

type MessageCounter struct {
	Mutex *sync.Mutex // mutex of the structure

	IDCounter  *uint64                  // current message ID
	PendingMsg map[uint64]*chan Message // list of current pending messages
}

MessageCounter structure containing message indexing tools

func (MessageCounter) GetMessageID

func (c MessageCounter) GetMessageID() uint64

GetMessageID get the next message ID, ids starting at 1

type NodeAddr

type NodeAddr struct {
	Addr   net.UDPAddr // udp address (ip + port) of the node
	NodeID Hash        // nodeID of that node
}

NodeAddr node address entry in the k-bucket, node udp ip and port, and nodeID

type Nonce

type Nonce [NONCESIZE]byte

Nonce format for encryption

func BytesToNonce

func BytesToNonce(array []byte) Nonce

BytesToNonce converts a byte array to a Nonce type.

type Passtor

type Passtor struct {
	Name   string // name of the passtor instance
	NodeID Hash   // hash of the name of the passtor, node identifier

	PConn      *net.UDPConn // udp socket to communicate with other passtors
	ClientAddr *net.TCPAddr // tcp address to communicate with clients

	Messages *MessageCounter // handles message id and pending messages

	Addr    NodeAddr           // address used to communicate with passtors
	Buckets map[uint16]*Bucket // k-buckets used in the DHT

	Printer Printer // passtor console printer

	Accounts Accounts
}

Passtor instance

func NewPasstor

func NewPasstor(name, addr string, verbose int) Passtor

NewPasstor creates and return a new Passtor instance

func (*Passtor) AddPeerToBucket

func (p *Passtor) AddPeerToBucket(addr NodeAddr)

AddPeerToBucket check if a peer should be added to the DHT, and if yes, add it to the appropriate bucket

func (*Passtor) Allocate

func (p *Passtor) Allocate(id Hash, repl uint32, data AccountNetwork) []NodeAddr

Allocate given data identified by the given id to the given replication factor appropriate peers

func (*Passtor) AllocateToPeer

func (p *Passtor) AllocateToPeer(id Hash, peer NodeAddr, index, repl uint32,
	data AccountNetwork) error

AllocateToPeer allocate some data to a peer, returns true on success, false if cannot reach peer or error

func (*Passtor) Delete

func (p *Passtor) Delete(id Hash)

Delete an account from a node storage

func (*Passtor) FetchData

func (p *Passtor) FetchData(h *Hash, threshold float64) *Account

FetchData request associated with given hash from the DHT

func (*Passtor) FetchDataFromPeer

func (p *Passtor) FetchDataFromPeer(h *Hash, peer NodeAddr) *Message

FetchDataFromPeer send fetch request to given peer, returns the reply of the remote host

func (*Passtor) GetBucketID

func (p *Passtor) GetBucketID(val *Hash) uint16

GetBucketID get the bucket identifier in which val belongs

func (*Passtor) GetKCloser

func (p *Passtor) GetKCloser(h *Hash) []NodeAddr

GetKCloser get the K closer nodes to given hash

func (*Passtor) HandleAllocation

func (p *Passtor) HandleAllocation(msg Message)

HandleAllocation handles an allocation on the remote peer

func (*Passtor) HandleClientMessage

func (p *Passtor) HandleClientMessage(accounts Accounts, message ClientMessage) *ServerResponse

func (*Passtor) HandleFetch

func (p *Passtor) HandleFetch(msg Message)

HandleFetch searches for requested file, send it if it finds it

func (*Passtor) HandleMessage

func (p *Passtor) HandleMessage(protobufed []byte)

HandleMessage handles incoming messages

func (*Passtor) JoinDHT

func (p *Passtor) JoinDHT(peers []net.UDPAddr)

JoinDHT passtor join the DHT connect to bootstrap peers given as argument lookup for self to fill k-buckets

func (*Passtor) ListenToClients

func (p *Passtor) ListenToClients()

func (*Passtor) ListenToPasstors

func (p *Passtor) ListenToPasstors()

ListenToPasstors listen on the udp connection used to communicate with other passtors, and distribute received messages to HandleMessage()

func (*Passtor) LookupRep

func (p *Passtor) LookupRep(req Message)

LookupRep handles a lookup request and reply to it

func (*Passtor) LookupReq

func (p *Passtor) LookupReq(hash *Hash) []NodeAddr

LookupReq lookup a hash

func (*Passtor) Ping

func (p *Passtor) Ping(peer net.UDPAddr, retries int) bool

Ping a remote node, tries at most the given number of times

func (*Passtor) PrintBuckets

func (p *Passtor) PrintBuckets()

PrintBuckets print all bucket with their state

func (*Passtor) Republish

func (p *Passtor) Republish(account AccountInfo)

Republish account information in the DHT, called periodically

func (*Passtor) SendMessage

func (p *Passtor) SendMessage(msg Message, dst net.UDPAddr,
	retries int) *Message

SendMessage send the given message to the remote peer over udp returns the reply message once it has arrived

func (*Passtor) SetIdentity

func (p *Passtor) SetIdentity()

SetIdentity set the identity of the passtor instance to the hash of the given name

func (*Passtor) Store

func (p *Passtor) Store(newAccount Account, repl uint32) error

Store an account on a node

type Printer

type Printer struct {
	Verbose    int
	Printer    *log.Logger
	ErrPrinter *log.Logger
}

Printer of the passtor, handles all prints to console

func (*Printer) Print

func (p *Printer) Print(str string, V int)

Print a message to stdout

func (*Printer) PrintErr

func (p *Printer) PrintErr(str string)

PrintErr prints error message to stderr

func (*Printer) WPrint

func (p *Printer) WPrint(str string, V int)

WPrint prints warnings

type PrivateKey

type PrivateKey = ed25519.PrivateKey

PrivateKey type

func SeedToPrivateKey

func SeedToPrivateKey(seed []byte) PrivateKey

type PublicKey

type PublicKey = ed25519.PublicKey

PublicKey type

type Salt

type Salt [SALTLENGTH]byte

func BytesToSalt

func BytesToSalt(array []byte) Salt

type Secret

type Secret = SymmetricKey

Secret is the secret used by the user to locally decrypt its symmetric key K and secret key sk

func ComputeSecret

func ComputeSecret(userID, masterPassword string, salt Salt) Secret

func KDFToSecret

func KDFToSecret(array []byte) Secret

type ServerResponse

type ServerResponse struct {
	Status string
	Debug  *string
	Data   *AccountNetwork
}

ServerResponse represents a response from a node to a client

type Signature

type Signature [SIGNATURESIZE]byte

Signature format

func BytesToSignature

func BytesToSignature(array []byte) Signature

func Sign

func Sign(data []byte, key PrivateKey) Signature

Sign computes the signature of the given message under the given private key

type SymmetricKey

type SymmetricKey [SYMMKEYSIZE]byte

SymmetricKey format

func BytesToSymmetricKey

func BytesToSymmetricKey(array []byte) SymmetricKey

BytesToSymmetricKey creates a symmetric key from an array of bytes

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL