// Encrypt returns an encrypted Keys blob. The format of the blob is // [salt][nonce][encrypted keys] func (k *Keys) Encrypt(password []byte, N, r, p int) ([]byte, error) { // encode Keys var keysXDR bytes.Buffer _, err := xdr.Marshal(&keysXDR, k) if err != nil { return nil, err } // generate a derived key var salt [KeySize]byte _, err = io.ReadFull(rand.Reader, salt[:]) if err != nil { return nil, err } dk, err := scrypt.Key(password, salt[:], N, r, p, KeySize) if err != nil { return nil, err } var key [KeySize]byte copy(key[:], dk) goutil.Zero(dk) go func() { goutil.Zero(key[:]) }() // encrypt KeySafe nonce, err := NaClNonce() if err != nil { return nil, err } ksEncrypted := secretbox.Seal(nil, keysXDR.Bytes(), nonce, &key) var blob bytes.Buffer w := bufio.NewWriter(&blob) // salt _, err = w.Write(salt[:]) if err != nil { return nil, err } // nonce _, err = w.Write(nonce[:]) if err != nil { return nil, err } // encrypted blob _, err = w.Write(ksEncrypted[:]) if err != nil { return nil, err } w.Flush() return blob.Bytes(), nil }
// uploadSecrets encrypts and uploads the secrets to acd for safe keeping. func (a *acdb) uploadSecrets() error { a.Log(acd.DebugTrace, "[TRC] uploadSecrets") fmt.Printf("Cloud Drive does not have a copy of the secrets. Please enter " + "the password to encrypt the secrets. Loss of this password is " + "unrecoverable!\n") p, err := shared.PromptPassword(true) if err != nil { return err } defer func() { goutil.Zero(p) }() blob, err := a.keys.Encrypt(p, 32768, 16, 2) if err != nil { return err } asset, err := a.c.UploadJSON(a.metadataID, secretsName, blob) if err != nil { if e, ok := acd.IsCombinedError(err); ok { if e.StatusCode != http.StatusConflict { return fmt.Errorf("secrets appeared unexpectedly") } } } a.Log(acd.DebugTrace, "[TRC] uploadSecrets object: %v", asset.ID) return nil }
func CreateNewKeys(filename string) error { k := Keys{} _, err := io.ReadFull(rand.Reader, k.MD[:]) if err != nil { return err } _, err = io.ReadFull(rand.Reader, k.Data[:]) if err != nil { return err } _, err = io.ReadFull(rand.Reader, k.Dedup[:]) if err != nil { return err } dir := path.Dir(filename) err = os.MkdirAll(dir, 0700) if err != nil { return err } f, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, 0600) defer func() { _ = f.Close() }() e := json.NewEncoder(f) err = e.Encode(k) if err != nil { return err } goutil.Zero(k.MD[:]) goutil.Zero(k.Data[:]) goutil.Zero(k.Dedup[:]) return nil }
func (a *acdb) downloadSecrets() error { a.Log(acd.DebugTrace, "[TRC] downloadSecrets") asset, err := a.c.GetMetadataFS(metadataName + "/" + secretsName) if err != nil { if err == acd.ErrNotFound { return a.uploadSecrets() } return fmt.Errorf("remote object not found") } a.Log(acd.DebugTrace, "[TRC] found asset: %v -> %v\n", asset.ID, asset.Name) blob, err := a.c.DownloadJSON(asset.ID) if err != nil { return err } var p []byte defer func() { goutil.Zero(p) }() for { p, err = shared.ReadPassword() if err == nil { break } if !os.IsNotExist(err) { return err } fmt.Printf("There is no local password file. Please enter " + "password to verify the integrity of the remote " + "secrets.\n") p, err = shared.PromptPassword(false) if err != nil { return err } err = a.verifySecrets(p, blob) if err != nil { fmt.Printf("invalid password: %v\n", err) continue } return shared.WritePassword(p) } return a.verifySecrets(p, blob) }
// KeysDecrypt decrypts keys from a blob. This function relies on secretbox's // property that it'll fail decryption due to authenticators. As such it does // not carry a digest to validate the contents. func KeysDecrypt(password []byte, N, r, p int, blob []byte) (*Keys, error) { var ( salt [KeySize]byte nonce [NonceSize]byte ) copy(salt[:], blob[0:KeySize]) copy(nonce[:], blob[KeySize:KeySize+NonceSize]) // key dk, err := scrypt.Key(password, salt[:], N, r, p, KeySize) if err != nil { return nil, err } var key [KeySize]byte copy(key[:], dk) goutil.Zero(dk) go func() { goutil.Zero(key[:]) }() ksXDR, ok := secretbox.Open(nil, blob[KeySize+NonceSize:], &nonce, &key) if !ok { return nil, fmt.Errorf("could not decrypt") } k := Keys{} _, err = xdr.Unmarshal(bytes.NewReader(ksXDR), &k) if err != nil { return nil, fmt.Errorf("could not unmarshal") } return &k, nil }
func PromptPassword(save bool) ([]byte, error) { var ( p1, p2 []byte err error ) defer func() { goutil.Zero(p2) }() for { fmt.Printf("Password: "******"\nAgain : ") p2, err = terminal.ReadPassword(0) if err != nil { return nil, err } fmt.Printf("\n") if bytes.Equal(p1, p2) && len(p1) != 0 { break } fmt.Printf("Passwords do not match or are empty.\n") } if save { err = WritePassword(p1) if err != nil { return nil, err } } return p1, nil }
func _main() error { // tar like create := flag.Bool("c", false, "create archive") // default *is* true extract := flag.Bool("x", false, "extract archive") lst := flag.Bool("t", false, "list archive contents") verbose := flag.Bool("v", false, "verbose") compress := flag.Bool("z", false, "enable compression (default false)") perms := flag.Bool("p", false, "restore ACL") target := flag.String("f", "-", "archive target is Cloud Drive)") root := flag.String("C", "", "extract path") // not tar like debugLevel := flag.Int("d", 0, "debug level: 0 off, 1 trace, 2 loud") debugTarget := flag.String("l", "-", "debug target file name, - is stdout") flag.Parse() args := flag.Args() var err error a := acdb{ permList: list.New(), target: *target, verbose: *verbose, compress: *compress, perms: *perms, root: *root, } defer func() { goutil.Zero(a.keys.MD[:]) goutil.Zero(a.keys.Data[:]) goutil.Zero(a.keys.Dedup[:]) }() // debug target if *debugTarget == "-" { a.Debugger, err = debug.NewDebugStdout() if err != nil { return err } } else { a.Debugger, err = debug.NewDebugFile(*debugTarget) if err != nil { return err } } switch *debugLevel { case 0: a.Debugger = debug.NewDebugNil() case 1: a.Debugger.Mask(acd.DebugTrace | acd.DebugHTTP | acd.DebugURL | debugApp) case 2: a.Debugger.Mask(acd.DebugTrace | acd.DebugHTTP | acd.DebugURL | acd.DebugBody | acd.DebugJSON | acd.DebugToken | acd.DebugLoud | debugApp) default: return fmt.Errorf("invalid debug level %v", *debugLevel) } //a.Debugger.Mask(acd.DebugTrace | acd.DebugHTTP | acd.DebugURL | //acd.DebugJSON | debugApp) a.Log(debugApp, "[APP] start of day") defer a.Log(debugApp, "[APP] end of times") // default to create if *create == false && *extract == false && *lst == false { *create = true } // determine operation switch { case *create && !(*extract || *lst): a.mode = modeCreate if len(args) == 0 { fmt.Printf("acdbackup <-c> [-vzf target] filenames...\n") flag.PrintDefaults() return nil } return a.archive(args) case *extract && !(*create || *lst): a.mode = modeExtract if a.target == "-" { return fmt.Errorf("must provide archive metadata file") } return a.list() case *lst && !(*create || *extract): a.mode = modeList if a.target == "-" { return fmt.Errorf("must provide archive metadata file") } return a.list() default: return fmt.Errorf("must specify only -c, -x or -t") } return nil }
func _main() error { debugLevel := flag.Int("d", 0, "debug level: 0 off, 1 trace, 2 loud") debugTarget := flag.String("l", "-", "debug target file name, - is stdout") compress := flag.Bool("c", false, "try to compress (default = false)") extract := flag.Bool("e", false, "extract files") flag.Parse() args := flag.Args() if len(args) == 0 { fmt.Printf("sfe [-d][-l target] <filename> ...\n") flag.PrintDefaults() return nil } var ( err error ) s := sfe{ compress: *compress, } defer func() { goutil.Zero(s.keys.MD[:]) goutil.Zero(s.keys.Data[:]) goutil.Zero(s.keys.Dedup[:]) }() // debug target if *debugTarget == "-" { s.Debugger, err = debug.NewDebugStdout() if err != nil { return err } } else { s.Debugger, err = debug.NewDebugFile(*debugTarget) if err != nil { return err } } switch *debugLevel { case 0: s.Debugger = debug.NewDebugNil() case 1: s.Debugger.Mask(dbgTrace) case 2: s.Debugger.Mask(dbgTrace | dbgLoud) default: return fmt.Errorf("invalid debug level %v", *debugLevel) } keysFilename, err := shared.DefaultKeysFilename() if err != nil { return err } rootDir := path.Dir(keysFilename) err = os.MkdirAll(rootDir, 0700) if err != nil { return err } err = shared.LoadKeys(keysFilename, &s.keys) if err != nil { return err } for _, v := range args { if *extract { s.Log(dbgTrace, "decrypting: %v\n", v) err = s.decrypt(v) if err != nil { fmt.Fprintf(os.Stderr, "could not decrypt: %v\n", err) continue } } else { s.Log(dbgTrace, "encrypting: %v\n", v) err = s.encrypt(v) if err != nil { fmt.Fprintf(os.Stderr, "could not encrypt: %v\n", err) continue } } } return nil }