func getPermissions(driveFile *gdrive.File) (os.FileMode, error) { permStr, err := driveFile.GetProperty("Permissions") if err != nil { return 0, err } perm, err := strconv.ParseInt(permStr, 8, 16) return os.FileMode(perm), err }
// Returns the initialization vector (for encryption) for the given file. // We store the initialization vector as a hex-encoded property in the // file so that we don't need to download the file's contents to find the // IV. func getInitializationVector(driveFile *gdrive.File) ([]byte, error) { ivhex, err := driveFile.GetProperty("IV") if err != nil { return nil, err } iv, err := hex.DecodeString(ivhex) if err != nil { return nil, err } if len(iv) != aes.BlockSize { return nil, fmt.Errorf("unexpected length of IV %d", len(iv)) } return iv, nil }
func checkFile(f *gdrive.File) int { hasSuffix := strings.HasSuffix(f.Path, encryptionSuffix) _, err := f.GetProperty("IV") hasIV := err == nil if hasSuffix && !hasIV { fmt.Fprintf(os.Stderr, "skicka: %s: has filename suffix \"%s\" but no IV property\n", f.Path, encryptionSuffix) return 1 } else if hasIV && !hasSuffix { fmt.Fprintf(os.Stderr, "skicka: %s: has IV property but no filename suffix \"%s\"\n", f.Path, encryptionSuffix) return 1 } return 0 }
// If a file is encrypted, it should both have the initialization vector used // to encrypt it stored as a Drive file property and have encryptionSuffix at the end // of its filename. This function checks both of these and returns an error if // these indicators are inconsistent; otherwise, it returns true/false // accordingly. func isEncrypted(file *gdrive.File) (bool, error) { if _, err := file.GetProperty("IV"); err == nil { if strings.HasSuffix(file.Path, encryptionSuffix) { return true, nil } return false, fmt.Errorf("has IV property but doesn't " + "end with .aes256 suffix") } else if strings.HasSuffix(file.Path, encryptionSuffix) { // This could actually happen with an interrupted upload // with 403 errors and the case where a file is created // even though a 403 happened, if we don't get to delete // the file before exiting... return false, fmt.Errorf("ends with .aes256 suffix but doesn't " + "have IV property") } return false, nil }
// If we didn't shut down cleanly before, there may be files that // don't have the various properties we expect. Check for that now // and patch things up as needed. func createMissingProperties(f *gdrive.File, mode os.FileMode, encrypt bool) error { if !f.IsFolder() && encrypt { if _, err := f.GetProperty("IV"); err != nil { if f.FileSize == 0 { // Compute a unique IV for the file. iv := getRandomBytes(aes.BlockSize) ivhex := hex.EncodeToString(iv) debug.Printf("Creating IV property for file %s, "+ "which doesn't have one.", f.Path) err := gd.AddProperty("IV", ivhex, f) if err != nil { return err } } else { // This is state of affairs really shouldn't ever happen, but // if somehow it does, it's important that we catch it: the // file is missing the IV property, but has some // contents. Presumably the IV is at the start of the file // contents and we could initialize the property from that // here, but any successfully created file should already have // the property, so we'll just error out, since it's not clear // what's going on here... return fmt.Errorf("encrypted file on Drive is missing" + "IV property, but has non-zero length. Can't create the IV " + "property without examining file contents.") } } } if _, err := f.GetProperty("Permissions"); err != nil { debug.Printf("Creating Permissions property for file %s, "+ "which doesn't have one.", f.Path) err := gd.AddProperty("Permissions", fmt.Sprintf("%#o", mode&os.ModePerm), f) if err != nil { return err } } return nil }