// ImportKeys imports the keys from the given file, replacing the current keys for this sync func (s *SyncInfo) ImportKeys(inPath, pw string) error { encoded, err := ioutil.ReadFile(inPath) if err != nil { return &ErrSync{"Unable to open key file", err} } encrypted, err := gocrypt.BytesFromB64(string(encoded)) if err != nil { return &ErrSync{"Unable to decode key file", err} } salt := encrypted[:aes.KeyLength] exportKeys := generatePbkdf2KeyCombo(pw, salt) decrypted := memstream.New() _, cnt, err := aes.Decrypt(bytes.NewBuffer(encrypted[aes.KeyLength:]), decrypted, exportKeys) if err != nil { return &ErrSync{"Unable to decrypt keys", err} } if cnt != int64(aes.KeyLength*2) { return &ErrSync{"Keys read, but do not appear to be the correct format", nil} } decrypted.Rewind() cryptoKey := gocrypt.Key(make([]byte, aes.KeyLength)) var read int if read, err = decrypted.Read(cryptoKey); err != nil && err != io.EOF { return &ErrSync{"Failed to read crypto key", err} } cryptoKey = cryptoKey[:read] authKey := gocrypt.Key(make([]byte, aes.KeyLength)) if read, err = decrypted.Read(authKey); err != nil && err != io.EOF { return &ErrSync{"Failed to read auth key", err} } authKey = authKey[:read] if len(cryptoKey) != aes.KeyLength || len(authKey) != aes.KeyLength { return &ErrSync{"Keys read, but they do not appear to be long enough", nil} } s.Set(cryptoKeyKey, cryptoKey.String()) s.Set(authKeyKey, authKey.String()) return nil }
// ExtractContents decrypts the contents of the protected file onto the local path func (h *Header) ExtractContents() error { // Skip to contents protFile, err := os.Open(h.AbsRemotePath()) if err != nil { return &ErrProtectedFile{"Unable to open protected file", err} } defer protFile.Close() _, err = protFile.Seek(int64(h.headerLen), 0) if err != nil { return &ErrProtectedFile{"Unable to skip to contents", err} } // Decrypt to temp tempFile, err := ioutil.TempFile("", "syncerdecryptedcontents") if err != nil { return &ErrProtectedFile{"Unable to create temp file", err} } defer func() { tempFile.Close() os.Remove(tempFile.Name()) }() _, _, err = aes.Decrypt(protFile, tempFile, h.contentKeys) if err != nil { return &ErrProtectedFile{"Error during decryption", err} } // Move to final location tempFile.Close() err = moveFile(tempFile.Name(), h.AbsLocalPath()) if err != nil { return &ErrProtectedFile{"Failed to move decrypted file to local", err} } return nil }
// OpenProtectedFile reads the header of the given protected file and returns the data // needed to work further with that file. func OpenProtectedFile(sync *SyncInfo, protFilePath string) (*Header, error) { f, err := os.Open(filepath.Join(sync.RemoteBase(), protFilePath)) if err != nil { return nil, err } defer f.Close() // Decrypt header headBytes := memstream.New() keys := sync.Keys() encHeadLen, _, err := aes.Decrypt(f, headBytes, &keys) if err != nil { return nil, err } header := new(Header) header.sync = sync header.headerLen = int(encHeadLen) header.remotePath = protFilePath header.contentKeys = new(gocrypt.KeyCombo) // Interpret headBytes.Rewind() buf := bufio.NewReader(headBytes) version, err := buf.ReadByte() if err != nil { return nil, &ErrProtectedFile{"Unable to read header version", err} } switch version { case 1: // Format: // 1 - version (currently at version 1) // 16 - crypto key for contents // 16 - auth key // 32 - hash of DECRYPTED contents // 8 - length of Path // <Variable> - local relative path header.contentKeys.CryptoKey, err = readKey(buf) if err != nil { return nil, &ErrProtectedFile{"Unable to read crypto key", err} } header.contentKeys.AuthKey, err = readKey(buf) if err != nil { return nil, &ErrProtectedFile{"Unable to read auth key", err} } header.contentHash, err = readFixedSize(buf, hash.Sha256Size) if err != nil { return nil, &ErrProtectedFile{"Unable to read content hash", err} } lenBytes, err := readFixedSize(buf, 8) pathLen := gocrypt.BytesToUint64(lenBytes) if err != nil { return nil, &ErrProtectedFile{"Unable to read path length", err} } pathBytes, err := readFixedSize(buf, int(pathLen)) header.localPath = string(pathBytes) if err != nil { return nil, &ErrProtectedFile{"Unable to read path", err} } default: return nil, &ErrProtectedFile{fmt.Sprintf("Version %v of header is not recognized", version), nil} } return header, nil }