mirror of
https://github.com/rclone/rclone.git
synced 2024-11-29 11:55:01 +01:00
Review random string/password generation
- factor password generation into lib/random.Password - call from appropriate places - choose appropriate use of random.String vs random.Password
This commit is contained in:
parent
beb8d5c134
commit
193c30d570
@ -19,8 +19,6 @@ canStream = false
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -44,6 +42,7 @@ import (
|
|||||||
"github.com/rclone/rclone/lib/dircache"
|
"github.com/rclone/rclone/lib/dircache"
|
||||||
"github.com/rclone/rclone/lib/oauthutil"
|
"github.com/rclone/rclone/lib/oauthutil"
|
||||||
"github.com/rclone/rclone/lib/pacer"
|
"github.com/rclone/rclone/lib/pacer"
|
||||||
|
"github.com/rclone/rclone/lib/random"
|
||||||
"github.com/rclone/rclone/lib/rest"
|
"github.com/rclone/rclone/lib/rest"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
@ -664,7 +663,7 @@ func (f *Fs) move(ctx context.Context, isFile bool, id, oldLeaf, newLeaf, oldDir
|
|||||||
// another directory to make sure we don't overwrite something
|
// another directory to make sure we don't overwrite something
|
||||||
// in the destination directory by accident
|
// in the destination directory by accident
|
||||||
if doRenameLeaf && doMove {
|
if doRenameLeaf && doMove {
|
||||||
tmpLeaf := newLeaf + "." + randomString(8)
|
tmpLeaf := newLeaf + "." + random.String(8)
|
||||||
err = f.renameLeaf(ctx, isFile, id, tmpLeaf)
|
err = f.renameLeaf(ctx, isFile, id, tmpLeaf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Move rename leaf")
|
return errors.Wrap(err, "Move rename leaf")
|
||||||
@ -1010,13 +1009,6 @@ func (o *Object) metaHash() string {
|
|||||||
return fmt.Sprintf("remote=%q, size=%d, modTime=%v, id=%q, mimeType=%q", o.remote, o.size, o.modTime, o.id, o.mimeType)
|
return fmt.Sprintf("remote=%q, size=%d, modTime=%v, id=%q, mimeType=%q", o.remote, o.size, o.modTime, o.id, o.mimeType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// randomString returns a string with 8*bytes bits of entropy
|
|
||||||
func randomString(bytes int) string {
|
|
||||||
var pw = make([]byte, bytes)
|
|
||||||
_, _ = rand.Read(pw)
|
|
||||||
return base64.RawURLEncoding.EncodeToString(pw)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the object with the contents of the io.Reader, modTime and size
|
// Update the object with the contents of the io.Reader, modTime and size
|
||||||
//
|
//
|
||||||
// If existing is set then it updates the object rather than creating a new one
|
// If existing is set then it updates the object rather than creating a new one
|
||||||
@ -1073,7 +1065,7 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
|
|||||||
uploaded := false
|
uploaded := false
|
||||||
var oldID = o.id
|
var oldID = o.id
|
||||||
if o.hasMetaData {
|
if o.hasMetaData {
|
||||||
newLeaf := leaf + "." + randomString(8)
|
newLeaf := leaf + "." + random.String(8)
|
||||||
fs.Debugf(o, "Moving old file out the way to %q", newLeaf)
|
fs.Debugf(o, "Moving old file out the way to %q", newLeaf)
|
||||||
err = o.fs.renameLeaf(ctx, true, oldID, newLeaf)
|
err = o.fs.renameLeaf(ctx, true, oldID, newLeaf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -64,7 +64,10 @@ See the [rc documentation](/rc/) for more info on the rc flags.
|
|||||||
fs.Infof("Using default username: %s \n", rcflags.Opt.HTTPOptions.BasicUser)
|
fs.Infof("Using default username: %s \n", rcflags.Opt.HTTPOptions.BasicUser)
|
||||||
}
|
}
|
||||||
if rcflags.Opt.HTTPOptions.BasicPass == "" {
|
if rcflags.Opt.HTTPOptions.BasicPass == "" {
|
||||||
randomPass := random.String(16)
|
randomPass, err := random.Password(128)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to make password: %v", err)
|
||||||
|
}
|
||||||
rcflags.Opt.HTTPOptions.BasicPass = randomPass
|
rcflags.Opt.HTTPOptions.BasicPass = randomPass
|
||||||
fs.Infof("No password specified. Using random password: %s \n", randomPass)
|
fs.Infof("No password specified. Using random password: %s \n", randomPass)
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ import (
|
|||||||
"github.com/rclone/rclone/fs/fshttp"
|
"github.com/rclone/rclone/fs/fshttp"
|
||||||
"github.com/rclone/rclone/fs/fspath"
|
"github.com/rclone/rclone/fs/fspath"
|
||||||
"github.com/rclone/rclone/fs/rc"
|
"github.com/rclone/rclone/fs/rc"
|
||||||
|
"github.com/rclone/rclone/lib/random"
|
||||||
"golang.org/x/crypto/nacl/secretbox"
|
"golang.org/x/crypto/nacl/secretbox"
|
||||||
"golang.org/x/text/unicode/norm"
|
"golang.org/x/text/unicode/norm"
|
||||||
)
|
)
|
||||||
@ -860,16 +861,10 @@ func ChooseOption(o *fs.Option, name string) string {
|
|||||||
for {
|
for {
|
||||||
fmt.Printf("Password strength in bits.\n64 is just about memorable\n128 is secure\n1024 is the maximum\n")
|
fmt.Printf("Password strength in bits.\n64 is just about memorable\n128 is secure\n1024 is the maximum\n")
|
||||||
bits := ChooseNumber("Bits", 64, 1024)
|
bits := ChooseNumber("Bits", 64, 1024)
|
||||||
bytes := bits / 8
|
password, err := random.Password(bits)
|
||||||
if bits%8 != 0 {
|
if err != nil {
|
||||||
bytes++
|
log.Fatalf("Failed to make password: %v", err)
|
||||||
}
|
}
|
||||||
var pw = make([]byte, bytes)
|
|
||||||
n, _ := rand.Read(pw)
|
|
||||||
if n != bytes {
|
|
||||||
log.Fatalf("password short read: %d", n)
|
|
||||||
}
|
|
||||||
password = base64.RawURLEncoding.EncodeToString(pw)
|
|
||||||
fmt.Printf("Your password is: %s\n", password)
|
fmt.Printf("Your password is: %s\n", password)
|
||||||
fmt.Printf("Use this password? Please note that an obscured version of this \npassword (and not the " +
|
fmt.Printf("Use this password? Please note that an obscured version of this \npassword (and not the " +
|
||||||
"password itself) will be stored under your \nconfiguration file, so keep this generated password " +
|
"password itself) will be stored under your \nconfiguration file, so keep this generated password " +
|
||||||
|
@ -1,9 +1,16 @@
|
|||||||
// Package random holds a few functions for working with random numbers
|
// Package random holds a few functions for working with random numbers
|
||||||
package random
|
package random
|
||||||
|
|
||||||
import "math/rand"
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
// String create a random string for test purposes
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// String create a random string for test purposes.
|
||||||
|
//
|
||||||
|
// Do not use these for passwords.
|
||||||
func String(n int) string {
|
func String(n int) string {
|
||||||
const (
|
const (
|
||||||
vowel = "aeiou"
|
vowel = "aeiou"
|
||||||
@ -20,3 +27,27 @@ func String(n int) string {
|
|||||||
}
|
}
|
||||||
return string(out)
|
return string(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Password creates a crypto strong password which is just about
|
||||||
|
// memorable. The password is composed of printable ASCII characters
|
||||||
|
// from the base64 alphabet.
|
||||||
|
//
|
||||||
|
// Requres password strength in bits.
|
||||||
|
// 64 is just about memorable
|
||||||
|
// 128 is secure
|
||||||
|
func Password(bits int) (password string, err error) {
|
||||||
|
bytes := bits / 8
|
||||||
|
if bits%8 != 0 {
|
||||||
|
bytes++
|
||||||
|
}
|
||||||
|
var pw = make([]byte, bytes)
|
||||||
|
n, err := rand.Read(pw)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err, "password read failed")
|
||||||
|
}
|
||||||
|
if n != bytes {
|
||||||
|
return "", errors.Errorf("password short read: %d", n)
|
||||||
|
}
|
||||||
|
password = base64.RawURLEncoding.EncodeToString(pw)
|
||||||
|
return password, nil
|
||||||
|
}
|
||||||
|
@ -4,10 +4,47 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestString(t *testing.T) {
|
func TestStringLength(t *testing.T) {
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
assert.Equal(t, i, len(String(i)))
|
s := String(i)
|
||||||
|
assert.Equal(t, i, len(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringDuplicates(t *testing.T) {
|
||||||
|
seen := map[string]bool{}
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
s := String(8)
|
||||||
|
assert.False(t, seen[s])
|
||||||
|
assert.Equal(t, 8, len(s))
|
||||||
|
seen[s] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPasswordLength(t *testing.T) {
|
||||||
|
for i := 0; i <= 128; i++ {
|
||||||
|
s, err := Password(i)
|
||||||
|
require.NoError(t, err)
|
||||||
|
// expected length is number of bytes rounded up
|
||||||
|
expected := i / 8
|
||||||
|
if i%8 != 0 {
|
||||||
|
expected++
|
||||||
|
}
|
||||||
|
// then converted to base 64
|
||||||
|
expected = (expected*8 + 5) / 6
|
||||||
|
assert.Equal(t, expected, len(s), i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPasswordDuplicates(t *testing.T) {
|
||||||
|
seen := map[string]bool{}
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
s, err := Password(64)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.False(t, seen[s])
|
||||||
|
seen[s] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user