Skip to content

Commit

Permalink
Stop using salt when deriving key from password (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
nakabonne committed Dec 28, 2020
1 parent 4d255f4 commit f2a81ac
Show file tree
Hide file tree
Showing 4 changed files with 16 additions and 101 deletions.
11 changes: 4 additions & 7 deletions commands/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,16 @@ func datasizeToBytes(ds string) (int64, error) {

// getSymmetricKey retrieves the symmetric-key. First try to derive it from password.
// Then try to read the file. errNotFound is returned if key not found.
func getSymmetricKey(password, symmetricKeyFile string, saltFunc func() ([]byte, error)) ([]byte, error) {
func getSymmetricKey(password, symmetricKeyFile string) ([]byte, error) {
if password != "" && (symmetricKeyFile != "" || os.Getenv(pbgopySymmetricKeyFileEnv) != "") {
return nil, fmt.Errorf("can't specify both password and key")
}

// Derive from password.
if password != "" {
var err error
salt, err := saltFunc()
if err != nil {
return nil, fmt.Errorf("failed to get salt: %w", err)
}
return pbcrypto.DeriveKey(password, salt), nil
// NOTE: This option is for cases where data cannot be shared between devices in advance.
// Therefore nil is used as a salt though it cannot prevent a dictionary attack.
return pbcrypto.DeriveKey(password, nil), nil
}

// Read from file.
Expand Down
37 changes: 8 additions & 29 deletions commands/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,23 +83,21 @@ func (r *copyRunner) run(_ *cobra.Command, _ []string) error {
return fmt.Errorf("failed to read from source: %w", err)
}

client := &http.Client{
Timeout: r.timeout,
}

data, err = r.encrypt(data, func() ([]byte, error) {
return r.regenerateSalt(client, address)
})
// Start encryption.
data, err = r.encrypt(data)
if err != nil {
return err
}

// Start issuing an HTTP request.
client := &http.Client{
Timeout: r.timeout,
}
req, err := http.NewRequest(http.MethodPut, address, bytes.NewBuffer(data))
if err != nil {
return fmt.Errorf("failed to make request: %w", err)
}
addBasicAuthHeader(req, r.basicAuth)

res, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to issue request: %w", err)
Expand All @@ -117,14 +115,14 @@ func (r *copyRunner) run(_ *cobra.Command, _ []string) error {
// - hybrid cryptosystem with a public-key
// - symmetric-key encryption with a key derived from password
// - symmetric-key encryption with an existing key
func (r *copyRunner) encrypt(plaintext []byte, saltFunc func() ([]byte, error)) ([]byte, error) {
func (r *copyRunner) encrypt(plaintext []byte) ([]byte, error) {
// Perform hybrid encryption with a public-key if specified.
if r.publicKeyFile != "" || r.gpgUserID != "" {
return r.encryptWithPubKey(plaintext)
}

// Try to encrypt with a symmetric-key.
key, err := getSymmetricKey(r.password, r.symmetricKeyFile, saltFunc)
key, err := getSymmetricKey(r.password, r.symmetricKeyFile)
if errors.Is(err, errNotfound) {
return plaintext, nil
}
Expand Down Expand Up @@ -182,22 +180,3 @@ func (r *copyRunner) encryptWithPubKey(plaintext []byte) ([]byte, error) {
EncryptedSessionKey: encryptedSessKey,
})
}

// regenerateSalt lets the server regenerate the salt and gives back the new one.
func (r *copyRunner) regenerateSalt(client *http.Client, address string) ([]byte, error) {
if strings.HasSuffix(address, "/") {
address = address[:len(address)-1]
}
req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("%s%s", address, saltPath), bytes.NewBuffer([]byte{}))
if err != nil {
return nil, fmt.Errorf("failed to make request: %w", err)
}
addBasicAuthHeader(req, r.basicAuth)
res, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to issue request: %w", err)
}
defer res.Body.Close()

return ioutil.ReadAll(res.Body)
}
32 changes: 4 additions & 28 deletions commands/paste.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"io/ioutil"
"net/http"
"os"
"strings"
"time"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -89,9 +88,8 @@ func (r *pasteRunner) run(_ *cobra.Command, _ []string) error {
return fmt.Errorf("failed to read the response body: %w", err)
}

data, err = r.decrypt(data, func() ([]byte, error) {
return r.getSalt(client, address)
})
// Start decryption if needed.
data, err = r.decrypt(data)
if err != nil {
return err
}
Expand All @@ -105,14 +103,14 @@ func (r *pasteRunner) run(_ *cobra.Command, _ []string) error {
// - hybrid cryptosystem with a private-key
// - symmetric-key encryption with a key derived from password
// - symmetric-key encryption with an existing key
func (r *pasteRunner) decrypt(data []byte, saltFunc func() ([]byte, error)) ([]byte, error) {
func (r *pasteRunner) decrypt(data []byte) ([]byte, error) {
// Perform hybrid decryption with a private-key if specified.
if r.privateKeyFile != "" || r.gpgUserID != "" {
return r.decryptWithPrivKey(data)
}

// Try to decrypt with a symmetric-key.
key, err := getSymmetricKey(r.password, r.symmetricKeyFile, saltFunc)
key, err := getSymmetricKey(r.password, r.symmetricKeyFile)
if errors.Is(err, errNotfound) {
return data, nil
}
Expand Down Expand Up @@ -175,26 +173,4 @@ func (r *pasteRunner) decryptWithPrivKey(data []byte) ([]byte, error) {
return nil, fmt.Errorf("failed to decrypt the encrypted data: %w", err)
}
return plaintext, nil

}

// getSalt gives back the salt.
func (r *pasteRunner) getSalt(client *http.Client, address string) ([]byte, error) {
if strings.HasSuffix(address, "/") {
address = address[:len(address)-1]
}
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s%s", address, saltPath), nil)
if err != nil {
return nil, fmt.Errorf("failed to make request: %w", err)
}
addBasicAuthHeader(req, r.basicAuth)
res, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to issue get request: %w", err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed request: Status %s", res.Status)
}
return ioutil.ReadAll(res.Body)
}
37 changes: 0 additions & 37 deletions commands/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package commands

import (
"context"
"crypto/rand"
"errors"
"fmt"
"io"
Expand All @@ -22,11 +21,9 @@ const (
defaultTTL = time.Hour * 24

rootPath = "/"
saltPath = "/salt"
lastUpdatedPath = "/lastupdated"

dataCacheKey = "data"
saltCacheKey = "salt"
lastUpdatedCacheKey = "lastUpdated"
)

Expand Down Expand Up @@ -90,7 +87,6 @@ func (r *serveRunner) newServer() *http.Server {
Handler: mux,
}
mux.HandleFunc(rootPath, r.basicAuthHandler(r.handle))
mux.HandleFunc(saltPath, r.basicAuthHandler(r.handleSalt))
mux.HandleFunc(lastUpdatedPath, r.basicAuthHandler(r.handleLastUpdated))
return server
}
Expand Down Expand Up @@ -132,39 +128,6 @@ func (r *serveRunner) handle(w http.ResponseWriter, req *http.Request) {
}
}

func (r *serveRunner) handleSalt(w http.ResponseWriter, req *http.Request) {
switch req.Method {
case http.MethodGet:
salt, err := r.cache.Get(saltCacheKey)
if errors.Is(err, cache.ErrNotFound) {
http.Error(w, "The salt not found", http.StatusNotFound)
return
}
if err != nil {
http.Error(w, "Failed to get salt from cache", http.StatusInternalServerError)
return
}
if s, ok := salt.([]byte); ok {
w.Write(s)
return
}
http.Error(w, fmt.Sprintf("The cached data is unknown type: %T", salt), http.StatusInternalServerError)
case http.MethodPut:
salt := make([]byte, 128)
if _, err := rand.Read(salt); err != nil {
http.Error(w, fmt.Sprintf("Failed to make salt: %v", err), http.StatusInternalServerError)
return
}
if err := r.cache.Put(saltCacheKey, salt); err != nil {
http.Error(w, fmt.Sprintf("Failed to cache: %v", err), http.StatusInternalServerError)
return
}
w.Write(salt)
default:
http.Error(w, fmt.Sprintf("Method %s is not allowed", req.Method), http.StatusMethodNotAllowed)
}
}

func (r *serveRunner) handleLastUpdated(w http.ResponseWriter, req *http.Request) {
switch req.Method {
case http.MethodGet:
Expand Down

0 comments on commit f2a81ac

Please sign in to comment.