cloudflare/cloudflared
Publicmirrored from https://github.com/cloudflare/cloudflaredAvailable
cmd/cloudflared/encrypter/encrypt.go
176lines · modecode
| 1 | // Package encrypter is suitable for encrypting messages you would like to securely share between two points. |
| 2 | // Useful for providing end to end encryption (E2EE). It uses Box (NaCl) for encrypting the messages. |
| 3 | // tldr is it uses Elliptic Curves (Curve25519) for the keys, XSalsa20 and Poly1305 for encryption. |
| 4 | // You can read more here https://godoc.org/golang.org/x/crypto/nacl/box. |
| 5 | // |
| 6 | // msg := []byte("super safe message.") |
| 7 | // alice, err := New("alice_priv_key.pem", "alice_pub_key.pem") |
| 8 | // if err != nil { |
| 9 | // log.Fatal(err) |
| 10 | // } |
| 11 | // |
| 12 | // bob, err := New("bob_priv_key.pem", "bob_pub_key.pem") |
| 13 | // if err != nil { |
| 14 | // log.Fatal(err) |
| 15 | // } |
| 16 | // encrypted, err := alice.Encrypt(msg, bob.PublicKey()) |
| 17 | // if err != nil { |
| 18 | // log.Fatal(err) |
| 19 | // } |
| 20 | // |
| 21 | // data, err := bob.Decrypt(encrypted, alice.PublicKey()) |
| 22 | // if err != nil { |
| 23 | // log.Fatal(err) |
| 24 | // } |
| 25 | // fmt.Println(string(data)) |
| 26 | package encrypter |
| 27 | |
| 28 | import ( |
| 29 | "bytes" |
| 30 | "crypto/rand" |
| 31 | "encoding/base64" |
| 32 | "encoding/pem" |
| 33 | "errors" |
| 34 | "io" |
| 35 | "os" |
| 36 | |
| 37 | "golang.org/x/crypto/nacl/box" |
| 38 | ) |
| 39 | |
| 40 | // Encrypter represents a keypair value with auxiliary functions to make |
| 41 | // doing encryption and decryption easier |
| 42 | type Encrypter struct { |
| 43 | privateKey *[32]byte |
| 44 | publicKey *[32]byte |
| 45 | } |
| 46 | |
| 47 | // New returns a new encrypter with initialized keypair |
| 48 | func New(privateKey, publicKey string) (*Encrypter, error) { |
| 49 | e := &Encrypter{} |
| 50 | pubKey, key, err := e.fetchOrGenerateKeys(privateKey, publicKey) |
| 51 | if err != nil { |
| 52 | return nil, err |
| 53 | } |
| 54 | e.privateKey, e.publicKey = key, pubKey |
| 55 | return e, nil |
| 56 | } |
| 57 | |
| 58 | // PublicKey returns a base64 encoded public key. Useful for transport (like in HTTP requests) |
| 59 | func (e *Encrypter) PublicKey() string { |
| 60 | return base64.URLEncoding.EncodeToString(e.publicKey[:]) |
| 61 | } |
| 62 | |
| 63 | // Decrypt data that was encrypted using our publicKey. It will use our privateKey and the sender's publicKey to decrypt |
| 64 | // data is an encrypted buffer of data, mostly like from the Encrypt function. Messages contain the nonce data on the front |
| 65 | // of the message. |
| 66 | // senderPublicKey is a base64 encoded version of the sender's public key (most likely from the PublicKey function). |
| 67 | // The return value is the decrypted buffer or an error. |
| 68 | func (e *Encrypter) Decrypt(data []byte, senderPublicKey string) ([]byte, error) { |
| 69 | var decryptNonce [24]byte |
| 70 | copy(decryptNonce[:], data[:24]) // we pull the nonce from the front of the actual message. |
| 71 | pubKey, err := e.decodePublicKey(senderPublicKey) |
| 72 | if err != nil { |
| 73 | return nil, err |
| 74 | } |
| 75 | decrypted, ok := box.Open(nil, data[24:], &decryptNonce, pubKey, e.privateKey) |
| 76 | if !ok { |
| 77 | return nil, errors.New("failed to decrypt message") |
| 78 | } |
| 79 | return decrypted, nil |
| 80 | } |
| 81 | |
| 82 | // Encrypt data using our privateKey and the recipient publicKey |
| 83 | // data is a buffer of data that we would like to encrypt. Messages will have the nonce added to front |
| 84 | // as they have to unique for each message shared. |
| 85 | // recipientPublicKey is a base64 encoded version of the sender's public key (most likely from the PublicKey function). |
| 86 | // The return value is the encrypted buffer or an error. |
| 87 | func (e *Encrypter) Encrypt(data []byte, recipientPublicKey string) ([]byte, error) { |
| 88 | var nonce [24]byte |
| 89 | if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { |
| 90 | return nil, err |
| 91 | } |
| 92 | |
| 93 | pubKey, err := e.decodePublicKey(recipientPublicKey) |
| 94 | if err != nil { |
| 95 | return nil, err |
| 96 | } |
| 97 | // This encrypts msg and adds the nonce to the front of the message, since the nonce has to be |
| 98 | // the same for encrypting and decrypting |
| 99 | return box.Seal(nonce[:], data, &nonce, pubKey, e.privateKey), nil |
| 100 | } |
| 101 | |
| 102 | // WriteKeys keys will take the currently initialized keypair and write them to provided filenames |
| 103 | func (e *Encrypter) WriteKeys(privateKey, publicKey string) error { |
| 104 | if err := e.writeKey(e.privateKey[:], "BOX PRIVATE KEY", privateKey); err != nil { |
| 105 | return err |
| 106 | } |
| 107 | return e.writeKey(e.publicKey[:], "PUBLIC KEY", publicKey) |
| 108 | } |
| 109 | |
| 110 | // fetchOrGenerateKeys will either load or create a keypair if it doesn't exist |
| 111 | func (e *Encrypter) fetchOrGenerateKeys(privateKey, publicKey string) (*[32]byte, *[32]byte, error) { |
| 112 | key, err := e.fetchKey(privateKey) |
| 113 | if os.IsNotExist(err) { |
| 114 | return box.GenerateKey(rand.Reader) |
| 115 | } else if err != nil { |
| 116 | return nil, nil, err |
| 117 | } |
| 118 | |
| 119 | pub, err := e.fetchKey(publicKey) |
| 120 | if os.IsNotExist(err) { |
| 121 | return box.GenerateKey(rand.Reader) |
| 122 | } else if err != nil { |
| 123 | return nil, nil, err |
| 124 | } |
| 125 | return pub, key, nil |
| 126 | } |
| 127 | |
| 128 | // writeKey will write a key to disk in DER format (it's a standard pem key) |
| 129 | func (e *Encrypter) writeKey(key []byte, pemType, filename string) error { |
| 130 | data := pem.EncodeToMemory(&pem.Block{ |
| 131 | Type: pemType, |
| 132 | Bytes: key, |
| 133 | }) |
| 134 | |
| 135 | f, err := os.Create(filename) |
| 136 | if err != nil { |
| 137 | return err |
| 138 | } |
| 139 | |
| 140 | _, err = f.Write(data) |
| 141 | if err != nil { |
| 142 | return err |
| 143 | } |
| 144 | |
| 145 | return nil |
| 146 | } |
| 147 | |
| 148 | // fetchKey will load a a DER formatted key from disk |
| 149 | func (e *Encrypter) fetchKey(filename string) (*[32]byte, error) { |
| 150 | f, err := os.Open(filename) |
| 151 | if err != nil { |
| 152 | return nil, err |
| 153 | } |
| 154 | buf := new(bytes.Buffer) |
| 155 | io.Copy(buf, f) |
| 156 | |
| 157 | p, _ := pem.Decode(buf.Bytes()) |
| 158 | if p == nil { |
| 159 | return nil, errors.New("Failed to decode key") |
| 160 | } |
| 161 | var newKey [32]byte |
| 162 | copy(newKey[:], p.Bytes) |
| 163 | |
| 164 | return &newKey, nil |
| 165 | } |
| 166 | |
| 167 | // decodePublicKey will base64 decode the provided key to the box representation |
| 168 | func (e *Encrypter) decodePublicKey(key string) (*[32]byte, error) { |
| 169 | pub, err := base64.URLEncoding.DecodeString(key) |
| 170 | if err != nil { |
| 171 | return nil, err |
| 172 | } |
| 173 | var newKey [32]byte |
| 174 | copy(newKey[:], pub) |
| 175 | return &newKey, nil |
| 176 | } |
| 177 | |