// RemoteHash returns the hash of the full protected file. This is an expensive // operation, as the value is not chached. func (h *Header) RemoteHash() (gocrypt.Hash, error) { f, err := os.Open(h.AbsRemotePath()) if err != nil { return nil, err } defer f.Close() return hash.Sha256(f) }
// 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 }