# A Dead man's Switch in Go

# Dead Men Tell No Tales

[repo](https://github.com/Mobilpadde/dead-mans-switch)

## Why

I found this [video](https://www.youtube.com/watch?v=b6LoNZXkwSA), and found the concept a rather intriguing. Though, instead of the whole switching it on before the encryption starts, is definitely not how I would do it. 

For me, a dead man's switch should be switched on by default, and only decrypt upon asked along with the proper password of course.

### The Idea

The whole plan is simple enough, let's make a small go-program to encrypt a file on start, then, when we receive the password it shall decrypt out file again. Such, as before you go out to do the all the *nastinesses*, you start the program.

Now, for *funzies* and making this a tad more secure and obscure we will be using an e-mail to unlock our file again.

## The Research

### The E-mail

First of all, we need a way to receive e-mails, this is definitely the hardest part. 

For this post, though, we will go with [*emersion*'s `go-imap`](https://github.com/emersion/go-imap).

### The Encryption

To be completely honest, I do not know much about encryption, hashes and decryption. So I will just **go** with what what seems like a great encryption algorithm, namely the `AES 256`.

> AES 256 is virtually impenetrable using brute-force methods. While a 56-bit DES key can be cracked in less than a day, AES would take billions of years to break using current computing technology. Hackers would be foolish to even attempt this type of attack. 
 - https://www.solarwindsmsp.com/blog/aes-256-encryption-algorithm

## The Code

### 32 characters

First we need to create a secure 32 character password for our AES:

`echo 'madco.me' | sha256sum | base64 | head -c 32 ; echo`

> Please, do not use `madco.me` as your password.

### Code

First, let's set some rights for our files that will be created. These are stolen from https://stackoverflow.com/a/42718395/754471.

```go
const (
	// https://stackoverflow.com/a/42718395/754471
	OS_READ       = 04
	OS_WRITE      = 02
	OS_USER_SHIFT = 6

	OS_USER_R = OS_READ << OS_USER_SHIFT
	OS_USER_W = OS_WRITE << OS_USER_SHIFT
)
```

I use this helper function a lot, to check for `errors`.

```go
func e(err error) {
	if err != nil {
		log.Fatalln(err)
	}
}
```

Now we need an `encrypt`-func

```go
func encrypt(key string, bytes []byte) []byte {
	c, err := aes.NewCipher([]byte(key))
	e(err)

	gcm, err := cipher.NewGCM(c)
	e(err)

	nonce := make([]byte, gcm.NonceSize())

	_, err = io.ReadFull(rand.Reader, nonce)
	e(err)

	return gcm.Seal(nonce, nonce, bytes, nil)
}
```

And  of course a way to decrypt our file again

```go
func decrypt(key, bytes []byte) ([]byte, error) {
	c, err := aes.NewCipher([]byte(key))
	if err != nil {
		return []byte(""), err
	}

	gcm, err := cipher.NewGCM(c)
	if err != nil {
		return []byte(""), err
	}

	nonceSize := gcm.NonceSize()
	if len(bytes) < nonceSize {
		return []byte(""), err
	}

	nonce, cipherText := bytes[:nonceSize], bytes[nonceSize:]
	plain, err := gcm.Open(nil, nonce, cipherText, nil)
	if err != nil {
		return []byte(""), err
	}

	return plain, nil
}
```

Great, now that these are done, let's build the e-mail checker

We use these two modules to access the gmail imap:

* github.com/emersion/go-imap/client
* github.com/emersion/go-imap

In here we read five messages, and return a true if we receive the `Unlock` subject along with the proper passphrase, we generated this [here](#32-characters). 

We then split our body of the e-mail into smaller strings, and use the third one as our code. I had to fiddle a bit to figure this. So try out yourselves if you want to checkout what values the body provides.

If we receive the proper code, we decrypt and write the back a new and decrypted version of the encrypted file.

```go
func checker(user, pass, file string, bytes []byte) bool {
	c, err := client.DialTLS("imap.gmail.com:993", nil)
	e(err)

	defer func() {
		err := c.Logout()
		e(err)
	}()

	err = c.Login(user, pass)
	e(err)

	mbox, err := c.Select("INBOX", true)
	e(err)

	seqset := new(imap.SeqSet)
	seqset.AddRange(mbox.Messages-5, mbox.Messages)

	section := &imap.BodySectionName{}
	items := []imap.FetchItem{section.FetchItem()}

	msgs := make(chan *imap.Message, 5)
	done := make(chan error, 1)

	go func() {
		done <- c.Fetch(seqset, items, msgs)
	}()

	for msg := range msgs {
		m, err := mail.ReadMessage(msg.GetBody(section))
		if err != nil {
			log.Println(err)
			break
		}

		subject := m.Header.Get("Subject")
		if subject == "Unlock" {
			body, err := ioutil.ReadAll(m.Body)
			if err != nil {
				log.Println(err)
				break
			}

			bodies := strings.Split(string(body), "\r\n")

			dat, err := decrypt([]byte(bodies[3]), bytes)
			e(err)

			orig := strings.Split(file, ".enc")[0]
			return ioutil.WriteFile(orig, dat, OS_USER_R|OS_USER_W) == nil
		}
	}

	return false
}
```

Finally we have the main func, in here we set two flags:

* `e`: To choose if the file has already been encrypted - which is great for testing, as the original file **will** be deleted upon running this program.
* `f`: The file to encrypt and remove.

Next-up we read in the lines from a pipe, I'll explain how to do this in after the code. Basically we read all the piped in lines and use those - There should always be - at least - three(**!**).

Finally we check the mail server every 2 seconds, just to set a limit.

```go
func main() {
	encryptFile := flag.Bool("e", false, "To encrypt before running?")
	file := flag.String("f", "./data.txt", "File to encrypt")

	flag.Parse()

	reader := bufio.NewReader(os.Stdin)

	var inputs []string
	for {
		input, _, err := reader.ReadLine()
		if err != nil {
			break
		}

		inputs = append(inputs, string(input))
	}

	base := filepath.Base(*file)
	dir := filepath.Dir(*file)

	var err error
	var encrypted []byte

	if *encryptFile {
		// If not encrypted, let's encrypt out file first
		fileBytes, err := ioutil.ReadFile(*file)
		e(err)

		key := inputs[0]
		encrypted = encrypt(key, fileBytes)

		err = ioutil.WriteFile(dir+"/"+base+".enc", encrypted, OS_USER_R|OS_USER_W)
		e(err)

		os.Remove(*file)
	} else {
		// Read the contents of the already encrypted file
		encrypted, err = ioutil.ReadFile(*file + ".enc")
		e(err)
	}

	mail, pass := inputs[1], inputs[2]
	for range time.Tick(2 * time.Second) {
		if succ := checker(mail, pass, *file+".enc", encrypted); succ {
			break
		}
	}
}
```

## Running

To run this program is quite easy.

We simply create a credentials file first - make sure this stays secret for absolutely everybody. This file should contain three lines:

* The 32 char secret
* Your e-mail
* An app password for your e-mail, as we always want to call back the password, if needed.

> To make an app password you simply navigate to this page: https://myaccount.google.com/apppasswords and sign-in. Then you choose `mail` and choose `Other`, give it a name and you'll see a password. Save this, as you won't be able to see it again.

## Prod?

You should not take this code as no way near production ready. But hack on it, and I believe in you! You'll make something out off this.

---

{ Best, [Mads Bram Cordes](https://buymeacoff.ee/mc) }
