minilock

package module
v0.0.0-...-fc22ad1 Latest Latest
Warning

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

Go to latest
Published: May 22, 2023 License: AGPL-3.0 Imports: 9 Imported by: 1

README

Go-miniLock

A pure-Go reimplementation of the miniLock asymmetric encryption system.

by Cathal Garvey, Copyright Oct. 2015, proudly licensed under the GNU AGPL.

Support via Gratipay

Or, Tip me a few bits? - 32ddsuR73CHH8igCNCLvRE3UwBqL8yU2ag

What

TL;DR: go-miniLock is a total Golang rewrite of miniLock, enabling native code performance, more platform flexibility, and downstream potential for automation and novel communication media not available to the original miniLock Chromium app.

See miniLock.io for information on miniLock. It's a file encryption system designed by Nadim Kobaissi and reviewed for security and soundness by experts. It's pretty well-put together, but as if that weren't enough it was released as an easy-to-use, user-focused Chrome App. In many respects it achieves what PGP was supposed to achieve, while dodging all the nastiness of PGP: Minilock gives:

  • Tiny identities: At ~40 characters, miniLock ID keys can be shared trivially through any medium.
  • Authenticated, Private communication between people without requiring a third-party
  • Easy verification of respective key:identity matchings.
  • Minimised metadata within the encrypted files; only recipients can see the identity of the sender and themselves, they cannot identify other valid recipients, and outsiders cannot determine, given a miniLock file, who sent it or who was the recipient.
  • No persistent keys: miniLock is designed to use deterministic keys that are generated from the user's memorable, highly secure passphrase and their email address (or a fake one..)
  • Transport agnostic: miniLock just encrypts files, it doesn't insist on a particular way of transmitting them.

The big disadvantage of miniLock has been how tied it is to Chrome; this limits platforms to laptops and desktops only, to GUI-enabled systems only, and makes integrating miniLock into other systems impossible. If you wanted to create a logging application that sends encrypted reports to your email daily using miniLock, forget it. If you wanted to build a P2P social network using miniLock for authentication and privacy, forget it.

See deadLock for my past efforts towards creating a shell-scriptable version of miniLock, but Python isn't much better than a Chrome app, due to version wars (thanks, fossilised 2.X users..), lack of pre-installation on Windows, and difficulty of C-extension compilation on WinMac. Oh, and the bug-prone-ness of Python in general!

Golang, as a language, addresses all the needs I forsee for a more versatile miniLock: it compiles to any platform extremely quickly, it has growing support for building native mobile apps, it can transpile to JS, it's fast, and it offers useful tools and concurrency primitives that facilitate the underlying, highly paralleliseable activity of miniLock. And, for a developer, it's very good at catching common bugs at compile-time; forgotten or renamed variables, typing errors, mismatched return types, etcetera.

So here's go-minilock; it sets out to be both an easy-to-use alternative to PGP, a native answer to the miniLock browser extension, and a library for easily constructing tools that go beyond manual human-to-human cryptography and extend into the automated, networked, or decentralised sphere.

Usage

Documentation for the library can be found at godoc. Functionality is deliberately broken into construction of the encrypted data itself and constructing the headers that assist in decryption and obfuscation of communicating parties; this is to enable use of the library for more than just miniLock-of-files, but also because other systems built atop miniLock (such as Peerio) use detached, updateable headers as a way to facilitate social file-sharing.

Much of the slightly-lower-level crypto stuff is in a sub-package called "taber", which can be imported separately with import "github.com/cryptag/go-minilock/taber", and documentation for which is here on Godoc.

For terminal usage of go-miniLock, you can install the tool with: go get -u github.com/cryptag/go-minilock/minilock-cli. Usage is simple enough and needs improvement:

minilock-cli encrypt <file> <your email> <recipient1> [<recipient2>...]
minilock-cli decrypt <file> <your email>

A number of flags modify usual behaviour. The most important is probably the "-p" flag which allows the passphrase for the user's key to be provided directly instead of being requested interactively; this allows shell-scripting using minilock-cli, or simply aliasing to create a rapid way of encrypting or decrypting things using your key. Beware, obviously, that for personal uses this breaks one of the security features of minilock, namely that personal keys are not stored but remembered! This feature, therefore, was more intended for server-side or scripting uses than for individuals.

A UI would be really nice but isn't yet on the cards. Watch this space. Meanwhile, use miniLock.

Where from Here

Here are things I'd really enjoy, if you're feeling creative. I may start on some of these, also..

  • Python bindings to go-miniLock, to enable a drastic refactor of deadlock. Current Go:Python binding solutions I've seen have involved some very ugly C shimming, but I suspect using FFI or Ctypes might work since Go 1.5 introduced C-ABI library compilation?
  • Integration of go-miniLock with desktop mail clients.
  • Transpiling usefully to JS using GopherJS, with a comparable library interface.
  • An Android client using the new Go:Android Bindings introduced in Go 1.5. Integration of said Android client into K9 Mail.
  • A self-hostable, federating Peerio server that respects your fundamental rights.
    • Bonus: Federates with other such servers in a robust way.
    • Bonus: Generates chaff traffic.
    • Bonus: Offers option to delete correspondance in same way as shared files.
    • Bonus: Talks to email servers, receives email and stores/delivers miniLock..and vice-versa.
  • A total rewrite of Peerio Client that doesn't require Chromium, could run headlessly.
    • Bonus: IMAP/SMTP adaptor 'client' for mail client alternative.
    • Bonus: IRC/XMPP adaptor 'client' for chat client alternatives.
    • Bonus: "Sync Folder Contents" option for dropbox-style crypto-extension to Peerio.
Credits Reel

Documentation

Overview

Package miniLock is a modern, authenticated, asymmetric encryption protocol that conceals metadata.

go-miniLock is a total Golang rewrite of miniLock.io, enabling native code performance, more platform flexibility, and downstream potential for automation and novel communication media not available to the original miniLock Chromium app.

go-minilock is copyright 2015, and proudly licensed under the GNU Affero General Public License, version 3 or later at your option.

To understand the code layout here, it may help to understand how minilock organises ciphertext. When creating ciphertext, minilock uses a symmetric cipher to encrypt chunks of the plaintext file, and indexes/length-prefixes each encrypted chunk. So, the raw ciphertext is a sliced and boxed version of the plaintext, using NaCL-style box encryption.

Minilock then puts some file metadata in a fileInfo datastructure including filename, file hash, and the symmetric cipher key, and encrypts this datastructure to an ephemeral key generated just for this ciphertext. This ephemeral key's private key component is then encrypted to each intended recipient, and each recipient's "decryptInfo" datastructure is added to the file header.

The resulting header, consisting of the fileInfo object and the various decryptInfo objects, is prepended to the raw ciphertext, and is in turn prepended with a Header length indicator and a magic 8-byte leader ("miniLock" in ascii). The result is a finished miniLock file.

This means that boxing or unwrapping a message has three stages of encryption/decryption, so the API of this library may at first seem daunting. One may well ask why I didn't hide all the messy details; firstly, I don't believe in hiding API details merely for aesthetics because I constantly encounter libraries that hide functions I genuinely need access to. Secondly, because there are other ways to use the fileInfo, decryptInfo and raw ciphertext objects that would require individual access to each level.

If you just want to encrypt a thing to a key, the function EncryptFileContentsWithStrings does everything at once; feed it your filename/contents, your chosen GUID (eg. your email), your passphrase, whether to include you as a recipient, and the miniLock IDs of all intended recipients, and you'll get back a finished miniLock file.

Likewise, to decrypt a miniLock file, just pass the miniLock file contents, your GUID/email, and your passphrase to DecryptFileContentsWithStrings, and you'll get back the sender's ID, filename, and file contents.

Please use responsibly; crypto is easy to do wrong and can do real harm if it exposes private information. I make no guarantees that my code is safe to use, in this sense.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrBadMagicBytes is returned when magic bytes didn't match expected 'miniLock'.
	ErrBadMagicBytes = errors.New("Magic bytes didn't match expected 'miniLock'")
	// ErrBadLengthPrefix is returned when header length exceeds file length.
	ErrBadLengthPrefix = errors.New("Header length exceeds file length")
	// ErrCTHashMismatch is returned when ciphertext hash did not match.
	ErrCTHashMismatch = errors.New("Ciphertext hash did not match")
	// ErrBadRecipient is returned when decryptInfo successfully decrypted but was addressed to another key.
	ErrBadRecipient = errors.New("DecryptInfo successfully decrypted but was addressed to another key")
	// ErrCannotDecrypt is returned when could not decrypt given ciphertext with given key or nonce.
	ErrCannotDecrypt = errors.New("Could not decrypt given ciphertext with given key or nonce")
	// ErrInsufficientEntropy is returned when got insufficient random bytes from RNG.
	ErrInsufficientEntropy = errors.New("Got insufficient random bytes from RNG")
	// ErrNilPlaintext is returned when got empty plaintext, can't encrypt.
	ErrNilPlaintext = errors.New("Got empty plaintext, can't encrypt")
)

Functions

func DecryptFileContents

func DecryptFileContents(fileContents []byte, recipientKey *taber.Keys) (senderID, filename string, contents []byte, err error)

DecryptFileContents parses header and ciphertext from a file, decrypts the header with recipientKey, and uses details therein to decrypt the enclosed file. Returns sender, filename, file contents if successful, or an error if not; Check the error to see if it's benign (cannot decrypt with given key) or bad.

func DecryptFileContentsWithStrings

func DecryptFileContentsWithStrings(fileContents []byte, recipientEmail, recipientPassphrase string) (senderID, filename string, contents []byte, err error)

DecryptFileContentsWithStrings is the highest-level API for decryption. It uses the recipient's email and passphrase to generate their key, attempts decryption, and wipes keys when finished.

func EncryptDecryptInfo

func EncryptDecryptInfo(di *DecryptInfoEntry, nonce []byte, ephemKey, recipientKey *taber.Keys) ([]byte, error)

EncryptDecryptInfo encrypts a decryptInfo struct using the ephemeral pubkey and the same nonce as the enclosed fileInfo.

func EncryptFileContents

func EncryptFileContents(filename string, fileContents []byte, sender *taber.Keys, recipients ...*taber.Keys) (miniLockContents []byte, err error)

EncryptFileContents is an entry point for encrypting byte slices from a prepared sender key to prepared recipient keys. EncryptFileContentsWithStrings is much easier to use for most applications.

func EncryptFileContentsWithStrings

func EncryptFileContentsWithStrings(filename string, fileContents []byte, senderEmail, senderPassphrase string, sendToSender bool, recipientIDs ...string) (miniLockContents []byte, err error)

EncryptFileContentsWithStrings is an entry point that largely defines "normal" miniLock behaviour. If sendToSender is true, then the sender's ID is added to recipients.

func EphemeralKey

func EphemeralKey() (*taber.Keys, error)

EphemeralKey generates a fully random key, usually for ephemeral uses.

func GenerateKey

func GenerateKey(email string, passphrase string) (*taber.Keys, error)

GenerateKey makes a key from an email address and passphrase, consistent with the miniLock algorithm. Passphrase is *not* currently checked for strength so it is, at present, the caller's responsibility to provide passphrases that don't suck!

func ImportID

func ImportID(id string) (*taber.Keys, error)

ImportID imports a miniLock ID as a public key.

func LoadKey

func LoadKey(private, public []byte) *taber.Keys

LoadKey manually loads a key from public and private binary strings.

func ParseFile

func ParseFile(filepath string) (header *miniLockv1Header, ciphertext []byte, err error)

ParseFile opens a file and passes to ParseFileContents

func ParseFileContents

func ParseFileContents(contents []byte) (header *miniLockv1Header, ciphertext []byte, err error)

ParseFileContents parses a miniLock file and returns header and ciphertext.

Types

type DecryptInfoEntry

type DecryptInfoEntry struct {
	SenderID    string `json:"senderID"`
	RecipientID string `json:"recipientID"`
	FileInfoEnc []byte `json:"fileInfo"`
}

DecryptInfoEntry is the container for the decryption instructions of "FileInfo", also containing sender and recipient. It is encrypted to the recipient with an ephemeral key to preserve privacy.

func DecryptDecryptInfo

func DecryptDecryptInfo(diEnc, nonce []byte, ephemPubkey, recipientKey *taber.Keys) (*DecryptInfoEntry, error)

DecryptDecryptInfo is used to extract a decryptInfo object by attempting decryption with a given recipientKey. This must be attempted for each decryptInfo in the header until one works or none work, as miniLock deliberately provides no indication of intended recipients.

func NewDecryptInfoEntry

func NewDecryptInfoEntry(nonce []byte, fileinfo *FileInfo, senderKey, recipientKey *taber.Keys) (*DecryptInfoEntry, error)

NewDecryptInfoEntry creates a decryptInfo entry for the given fileInfo to the intended recipientKey, from senderKey.

func (*DecryptInfoEntry) ExtractFileInfo

func (di *DecryptInfoEntry) ExtractFileInfo(nonce []byte, recipientKey *taber.Keys) (*FileInfo, error)

ExtractFileInfo pulls out the fileInfo object from the decryptInfo object, authenticating encryption from the sender.

func (*DecryptInfoEntry) SenderPubkey

func (die *DecryptInfoEntry) SenderPubkey() (*taber.Keys, error)

SenderPubkey returns the pubkey of the sender who (allegedly) created this DecryptInfoEntry.

type FileInfo

type FileInfo struct {
	FileKey   []byte `json:"fileKey"`
	FileNonce []byte `json:"fileNonce"`
	FileHash  []byte `json:"fileHash"`
}

FileInfo is the marrow at the heart of the miniLock header that contains decryption instructions for the encrypted file container.

func EncryptFileToFileInfo

func EncryptFileToFileInfo(filename string, filecontents []byte) (FI *FileInfo, ciphertext []byte, err error)

EncryptFileToFileInfo symmetrically encrypts a file or plaintext and returns the fileInfo object to decrypt it and the raw ciphertext. This operation is technically independent of miniLock and could be used for other crypto-schemes as a handy way to encrypt files symmetrically.

func (*FileInfo) DecryptFile

func (fi *FileInfo) DecryptFile(ciphertext []byte) (filename string, contents []byte, err error)

DecryptFile - Given a ciphertext, walk it into length prefixed chunks and decrypt/reassemble each chunk, then validate the hash of the file against the hash given in FileInfo. The result is a validated, decrypted filename and file contents byte-slice.

Directories

Path Synopsis
Package taber - for all your NaCL needs.
Package taber - for all your NaCL needs.

Jump to

Keyboard shortcuts

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