// ExportKeys exports this sync's keys to the given path, protecting them with // the given password. func (s *SyncInfo) ExportKeys(outPath, pw string) error { salt, err := gocrypt.SecureBytes(aes.KeyLength) if err != nil { return &ErrSync{"Unable to get salt for export", err} } exportKeys := generatePbkdf2KeyCombo(pw, salt) origBuf := memstream.New() origBuf.Write(s.Keys().CryptoKey) origBuf.Write(s.Keys().AuthKey) origBuf.Rewind() encryptedBuf := memstream.New() encryptedBuf.Write(salt) _, _, err = aes.Encrypt(origBuf, encryptedBuf, exportKeys) if err != nil { return &ErrSync{"Unable to encrypt keys", err} } encoded := gocrypt.BytesToB64(encryptedBuf.Bytes()) err = ioutil.WriteFile(outPath, []byte(encoded), 0770) if err != nil { return &ErrSync{"Unable to open key file", err} } return nil }
// Write rebuilds the protected file on disk, ensuring the contents and header // are current with the local file func (h *Header) Write() error { // Get data from local file for header localFile, err := os.Open(h.AbsLocalPath()) if err != nil { return &ErrProtectedFile{"Unable to open local file", err} } defer localFile.Close() h.contentHash, err = hash.Sha256(localFile) if err != nil { return &ErrProtectedFile{"Unable to get local file hash", err} } // Keys h.contentKeys, err = aes.NewKeyCombo() if err != nil { return &ErrProtectedFile{"Failed to generate keys for file", err} } // Write header to temp file headerBytes := memstream.NewCapacity(1024) writer := bufio.NewWriter(headerBytes) err = writer.WriteByte(1) // Version if err != nil { return &ErrProtectedFile{"Unable to write version", err} } _, err = writer.Write(h.contentKeys.CryptoKey) if err != nil { return &ErrProtectedFile{"Unable to write crypto key", err} } _, err = writer.Write(h.contentKeys.AuthKey) if err != nil { return &ErrProtectedFile{"Unable to write auth key", err} } _, err = writer.Write(h.contentHash) if err != nil { return &ErrProtectedFile{"Unable to write hash", err} } localLenBytes := gocrypt.Uint64ToBytes(uint64(len(h.LocalPath()))) _, err = writer.Write(localLenBytes) if err != nil { return &ErrProtectedFile{"Unable to write path length", err} } _, err = writer.Write([]byte(h.LocalPath())) if err != nil { return &ErrProtectedFile{"Unable to write local path", err} } if err = writer.Flush(); err != nil { return &ErrProtectedFile{"Flushing header failed", err} } // Encrypt and write header to temp file protFile, err := ioutil.TempFile("", "syncerprotfile") if err != nil { return &ErrProtectedFile{"Unable to create temp protected file", err} } defer func() { protFile.Close() os.Remove(protFile.Name()) }() headerBytes.Rewind() metakeys := h.sync.Keys() _, _, err = aes.Encrypt(headerBytes, protFile, &metakeys) if err != nil { return &ErrProtectedFile{"Unable to encrypt header", err} } // Update length of header. Not in file, just used internally for extracting headLen, err := protFile.Seek(0, 1) h.headerLen = int(headLen) if err != nil { return &ErrProtectedFile{"Unable to determine length of header", err} } // Encrypt and append contents _, err = localFile.Seek(0, 0) if err != nil { return &ErrProtectedFile{"Unable to return to beginning of local file", err} } _, _, err = aes.Encrypt(localFile, protFile, h.contentKeys) if err != nil { return &ErrProtectedFile{"Unable to encrypt contents", err} } // Move file to final location // TODO create any needed directories? protFile.Close() err = moveFile(protFile.Name(), h.AbsRemotePath()) if err != nil { return &ErrProtectedFile{"Failed to move final protected file", err} } return nil }