diff options
| -rw-r--r-- | internal/api/api.go | 4 | ||||
| -rw-r--r-- | internal/cryptoutil/oae2.go | 113 | ||||
| -rw-r--r-- | roseh.moe.go | 147 | ||||
| -rw-r--r-- | tools/notes/notes.go | 6 |
4 files changed, 98 insertions, 172 deletions
diff --git a/internal/api/api.go b/internal/api/api.go index 88a0011..6c9ab3a 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -38,6 +38,7 @@ type ListNotesResponse struct { type CreateNoteRequest struct { ContinuationToken []byte + FileName string Chunk []byte More bool } @@ -52,7 +53,8 @@ type ReadNoteRequest struct { } type ReadNoteResponseStream struct { - Chunk []byte + FileName string + Chunk []byte } type DeleteNoteRequest struct{} diff --git a/internal/cryptoutil/oae2.go b/internal/cryptoutil/oae2.go index a230c06..bc8f53e 100644 --- a/internal/cryptoutil/oae2.go +++ b/internal/cryptoutil/oae2.go @@ -11,7 +11,6 @@ import ( "encoding/binary" "errors" "io" - "strings" ) // Online Authenticated Encryption from https://eprint.iacr.org/2015/189.pdf @@ -217,77 +216,6 @@ func (w *EncryptingWriter) Close() error { return nil } -func (w *EncryptingWriter) ReadFrom(r io.Reader) (int64, error) { - if !w.initialized { - if err := w.initialize(); err != nil { - return 0, err - } - } - if w.err != nil { - return 0, w.err - } - var nn int64 - bufReader := bufio.NewReaderSize(r, 1) - for { - if _, err := bufReader.Peek(1); err != nil { - if err == io.EOF { - return nn, nil - } - return nn, err - } - if len(w.buf) == w.segmentSize { - if err := w.writeBuf(); err != nil { - return nn, err - } - } - n, _ := io.ReadFull(bufReader, w.buf[len(w.buf):w.segmentSize]) - w.buf = w.buf[:len(w.buf)+n] - nn += int64(n) - } -} - -func (w *EncryptingWriter) WriteByte(c byte) error { - if !w.initialized { - if err := w.initialize(); err != nil { - return err - } - } - if w.err != nil { - return w.err - } - if len(w.buf) == w.segmentSize { - if err := w.writeBuf(); err != nil { - return err - } - } - w.buf = append(w.buf, c) - return nil -} - -func (w *EncryptingWriter) WriteString(s string) (int, error) { - if !w.initialized { - if err := w.initialize(); err != nil { - return 0, err - } - } - if w.err != nil { - return 0, w.err - } - r := strings.NewReader(s) - nn := 0 - for r.Len() > 0 { - if len(w.buf) == w.segmentSize { - if err := w.writeBuf(); err != nil { - return nn, err - } - } - n, _ := r.Read(w.buf[len(w.buf):w.segmentSize]) - w.buf = w.buf[:len(w.buf)+n] - nn += n - } - return nn, nil -} - // A DecryptingReader decrypts data using the STREAM construction. type DecryptingReader struct { r *bufio.Reader @@ -367,44 +295,3 @@ func (r *DecryptingReader) Read(buf []byte) (int, error) { n, _ := r.buf.Read(buf) return n, nil } - -func (r *DecryptingReader) WriteTo(w io.Writer) (int64, error) { - if !r.initialized { - if err := r.initialize(); err != nil { - return 0, err - } - } - var nn int64 - if r.buf.Len() > 0 { - n, err := w.Write(r.buf.Bytes()) - nn += int64(n) - if err != nil { - return nn, err - } - } - for { - if err := r.fillBuf(); err != nil { - return nn, err - } - n, err := w.Write(r.buf.Bytes()) - nn += int64(n) - if err != nil { - return nn, err - } - } -} - -func (r *DecryptingReader) ReadByte() (byte, error) { - if !r.initialized { - if err := r.initialize(); err != nil { - return 0, err - } - } - if r.buf.Len() == 0 { - if err := r.fillBuf(); err != nil { - return 0, err - } - } - b, _ := r.buf.ReadByte() - return b, nil -} diff --git a/roseh.moe.go b/roseh.moe.go index 988938d..8c7e176 100644 --- a/roseh.moe.go +++ b/roseh.moe.go @@ -1,6 +1,7 @@ package main import ( + "bufio" "bytes" "crypto/rand" "crypto/subtle" @@ -16,6 +17,7 @@ import ( "io" "log" "net/http" + "net/url" "os" "path/filepath" "strconv" @@ -233,33 +235,6 @@ func setEncryptionKey(rawKey cryptoutil.RawKey) error { return nil } -type redirectTarget int - -const ( - redirectDefault redirectTarget = iota - redirectNotepad - redirectUpload -) - -func parseRedirectTarget(s string) redirectTarget { - i, err := strconv.Atoi(s) - if err != nil { - return redirectDefault - } - return redirectTarget(i) -} - -func (t redirectTarget) path() string { - switch t { - case redirectNotepad: - return "/notepad" - case redirectUpload: - return "/notes/upload" - default: - return "/" - } -} - var ( //go:embed templates/login.html.template loginString string @@ -268,13 +243,19 @@ var ( type loginTemplateArgs struct { Error bool - Redirect redirectTarget + Redirect string +} + +func executeLoginTemplate(w io.Writer, redirect string) { + if err := loginTemplate.Execute(w, loginTemplateArgs{Redirect: base64.RawURLEncoding.EncodeToString(secretKey.Sign([]byte(redirect), "redirect"))}); err != nil { + log.Printf("Warning: login: %s", err) + } } func login(w http.ResponseWriter, r *http.Request) { rawKey, err := notepadPassword.CheckPassword(r.FormValue("password")) if err != nil { - if err := loginTemplate.Execute(w, loginTemplateArgs{Error: true, Redirect: parseRedirectTarget(r.FormValue("redirect"))}); err != nil { + if err := loginTemplate.Execute(w, loginTemplateArgs{Error: true, Redirect: r.FormValue("redirect")}); err != nil { log.Printf("Warning: login: %s", err) } return @@ -284,7 +265,13 @@ func login(w http.ResponseWriter, r *http.Request) { return } attachCookie(w) - http.Redirect(w, r, parseRedirectTarget(r.FormValue("redirect")).path(), http.StatusSeeOther) + redirect := "/" + if b, err := base64.RawURLEncoding.DecodeString(r.FormValue("redirect")); err == nil { + if r, ok := secretKey.Verify(b, "redirect"); ok { + redirect = string(r) + } + } + http.Redirect(w, r, redirect, http.StatusSeeOther) } func readNotepad(key cryptoutil.EncryptionKey) (string, error) { @@ -314,18 +301,14 @@ type notepadTemplateArgs struct { func notepad(w http.ResponseWriter, r *http.Request) { csrfToken, ok := cookieAuth(w, r) if !ok { - if err := loginTemplate.Execute(w, loginTemplateArgs{Redirect: redirectNotepad}); err != nil { - log.Printf("Warning: login: %s", err) - } + executeLoginTemplate(w, "/notepad") return } encryptionKeyMu.Lock() key := encryptionKey encryptionKeyMu.Unlock() if key == nil { - if err := loginTemplate.Execute(w, loginTemplateArgs{Redirect: redirectNotepad}); err != nil { - log.Printf("Warning: login: %s", err) - } + executeLoginTemplate(w, "/notepad") return } currentContent, err := readNotepad(key) @@ -529,6 +512,14 @@ func createNote(req *api.CreateNoteRequest) (*api.CreateNoteResponse, error) { } } encryptingWriter := key.NewWriter(f, cryptoutil.WithAdditionalData([]byte("chunks/"+name))) + if contToken.I == 0 { + if _, err := encryptingWriter.Write(binary.AppendVarint(nil, int64(len(req.FileName)))); err != nil { + return nil, err + } + if _, err := io.WriteString(encryptingWriter, req.FileName); err != nil { + return nil, err + } + } if _, err := encryptingWriter.Write(req.Chunk); err != nil { return nil, err } @@ -577,13 +568,15 @@ func createNote(req *api.CreateNoteRequest) (*api.CreateNoteResponse, error) { } type readNoteResponseWriter struct { - stream *encoder + fileName string + stream *encoder } func (w *readNoteResponseWriter) Write(buf []byte) (int, error) { - if err := w.stream.send(&api.ReadNoteResponseStream{Chunk: buf}); err != nil { + if err := w.stream.send(&api.ReadNoteResponseStream{FileName: w.fileName, Chunk: buf}); err != nil { return 0, err } + w.fileName = "" return len(buf), nil } @@ -602,7 +595,16 @@ func readNote(stream *encoder, req *api.ReadNoteRequest) error { if key == nil { return fmt.Errorf("%w: need login", api.PermissionDenied) } - _, err = io.Copy(&readNoteResponseWriter{stream: stream}, key.NewReader(f, cryptoutil.WithAdditionalData([]byte("notes/"+req.Note)))) + reader := bufio.NewReader(key.NewReader(f, cryptoutil.WithAdditionalData([]byte("notes/"+req.Note)))) + nameLen, err := binary.ReadVarint(reader) + if err != nil { + return err + } + fileName := make([]byte, nameLen) + if _, err := io.ReadFull(reader, fileName); err != nil { + return err + } + _, err = io.Copy(&readNoteResponseWriter{fileName: string(fileName), stream: stream}, reader) return err } @@ -671,18 +673,14 @@ type uploadTemplateArgs struct { func uploadPage(w http.ResponseWriter, r *http.Request) { csrfToken, ok := cookieAuth(w, r) if !ok { - if err := loginTemplate.Execute(w, loginTemplateArgs{Redirect: redirectUpload}); err != nil { - log.Printf("Warning: login: %s", err) - } + executeLoginTemplate(w, "/upload") return } encryptionKeyMu.Lock() key := encryptionKey encryptionKeyMu.Unlock() if key == nil { - if err := loginTemplate.Execute(w, loginTemplateArgs{Redirect: redirectUpload}); err != nil { - log.Printf("Warning: login: %s", err) - } + executeLoginTemplate(w, "/upload") return } if err := uploadTemplate.Execute(w, uploadTemplateArgs{CSRFToken: csrfToken}); err != nil { @@ -696,20 +694,14 @@ func uploadNote(w http.ResponseWriter, r *http.Request) { return } if _, ok := cookieAuth(w, r); !ok { - log.Printf("asdf: bad auth") - if err := loginTemplate.Execute(w, loginTemplateArgs{Redirect: redirectUpload}); err != nil { - log.Printf("Warning: login: %s", err) - } + executeLoginTemplate(w, "/upload") return } encryptionKeyMu.Lock() key := encryptionKey encryptionKeyMu.Unlock() if key == nil { - log.Printf("asdf: key nil") - if err := loginTemplate.Execute(w, loginTemplateArgs{Redirect: redirectUpload}); err != nil { - log.Printf("Warning: login: %s", err) - } + executeLoginTemplate(w, "/upload") return } noteFile, err := newNoteName() @@ -718,12 +710,20 @@ func uploadNote(w http.ResponseWriter, r *http.Request) { return } defer noteFile.Close() - contentReader, _, err := r.FormFile("content") + contentReader, metadata, err := r.FormFile("content") if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } writer := key.NewWriter(noteFile, cryptoutil.WithAdditionalData([]byte("notes/"+filepath.Base(noteFile.Name())))) + if _, err := writer.Write(binary.AppendVarint(nil, int64(len(metadata.Filename)))); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if _, err := io.WriteString(writer, metadata.Filename); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } if _, err := io.Copy(writer, contentReader); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return @@ -739,6 +739,39 @@ func uploadNote(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "upload! pog\n%s", filepath.Base(noteFile.Name())) } +func readNoteContent(w http.ResponseWriter, r *http.Request) { + if _, ok := cookieAuth(w, r); !ok { + executeLoginTemplate(w, "/note/"+r.PathValue("note")) + return + } + f, err := os.OpenInRoot(*notepadDir+"/notes", r.PathValue("note")) + if err != nil { + notFound(w, r) + return + } + defer f.Close() + encryptionKeyMu.Lock() + key := encryptionKey + encryptionKeyMu.Unlock() + if key == nil { + executeLoginTemplate(w, "/note/"+r.PathValue("note")) + return + } + reader := bufio.NewReader(key.NewReader(f, cryptoutil.WithAdditionalData([]byte("notes/"+r.PathValue("note"))))) + nameLen, err := binary.ReadVarint(reader) + if err != nil { + http.Error(w, "invalid file", http.StatusInternalServerError) + return + } + fileName := make([]byte, nameLen) + if _, err := io.ReadFull(reader, fileName); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Header().Set("Content-Disposition", "attachment; filename*=UTF-8''"+url.QueryEscape(string(fileName))) + io.Copy(w, reader) +} + func main() { flag.Parse() @@ -751,9 +784,9 @@ func main() { http.HandleFunc("POST /login", login) http.HandleFunc("GET /notepad", notepad) http.HandleFunc("POST /notepad/autosave", autosave) - http.HandleFunc("GET /notes/upload", uploadPage) - http.HandleFunc("POST /notes/upload", uploadNote) - // http.HandleFunc("GET /notes/{note}", readNoteContent) + http.HandleFunc("GET /upload", uploadPage) + http.HandleFunc("POST /upload", uploadNote) + http.HandleFunc("GET /notes/{note}", readNoteContent) http.HandleFunc("POST /api/login", apiLogin) http.HandleFunc("GET /api/list-notes", gobReqRespMiddleware(listNotes)) http.HandleFunc("POST /api/create-note", gobReqRespMiddleware(createNote)) diff --git a/tools/notes/notes.go b/tools/notes/notes.go index 9f190f6..7714a89 100644 --- a/tools/notes/notes.go +++ b/tools/notes/notes.go @@ -10,6 +10,7 @@ import ( "io" "net/http" "os" + "path/filepath" "time" "github.com/google/subcommands" @@ -213,6 +214,7 @@ func (*newCommand) SetFlags(*flag.FlagSet) {} type createNoteRequestStreamWriter struct { ctx context.Context + fileName string continuationToken []byte } @@ -225,6 +227,7 @@ func (w *createNoteRequestStreamWriter) Write(buf []byte) (int, error) { for i := 0; i < 3; i++ { resp, err = createNote(w.ctx, &api.CreateNoteRequest{ ContinuationToken: w.continuationToken, + FileName: w.fileName, Chunk: buf, More: true, }) @@ -242,6 +245,7 @@ func (w *createNoteRequestStreamWriter) Write(buf []byte) (int, error) { if err != nil { return 0, err } + w.fileName = "" w.continuationToken = resp.ContinuationToken return len(buf), nil } @@ -252,7 +256,7 @@ func (*newCommand) new(ctx context.Context, fileName string) error { return err } defer f.Close() - streamWriter := &createNoteRequestStreamWriter{ctx: ctx} + streamWriter := &createNoteRequestStreamWriter{ctx: ctx, fileName: filepath.Base(fileName)} if _, err := io.Copy(streamWriter, f); err != nil { return err } |
