diff --git a/pkg/cli/cmd/sync/sync.go b/pkg/cli/cmd/sync/sync.go
index 28ec71a7..ec66a2cb 100644
--- a/pkg/cli/cmd/sync/sync.go
+++ b/pkg/cli/cmd/sync/sync.go
@@ -97,8 +97,7 @@ func (l syncList) getLength() int {
return len(l.Notes) + len(l.Books) + len(l.ExpungedNotes) + len(l.ExpungedBooks)
}
-// processFragments categorizes items in sync fragments into a sync list. It also decrypts any
-// encrypted data in sync fragments.
+// processFragments categorizes items in sync fragments into a sync list.
func processFragments(fragments []client.SyncFragment) (syncList, error) {
notes := map[string]client.SyncFragNote{}
books := map[string]client.SyncFragBook{}
diff --git a/pkg/cli/crypt/crypto.go b/pkg/cli/crypt/crypto.go
deleted file mode 100644
index 3637c7ca..00000000
--- a/pkg/cli/crypt/crypto.go
+++ /dev/null
@@ -1,123 +0,0 @@
-/* Copyright (C) 2019, 2020, 2021, 2022, 2023, 2024, 2025 Dnote contributors
- *
- * This file is part of Dnote.
- *
- * Dnote is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Dnote is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Dnote. If not, see .
- */
-
-// Package crypt provides cryptographic funcitonalities
-package crypt
-
-import (
- "crypto/aes"
- "crypto/cipher"
- "crypto/rand"
- "crypto/sha256"
- "encoding/base64"
- "io"
-
- "github.com/pkg/errors"
- "golang.org/x/crypto/hkdf"
- "golang.org/x/crypto/pbkdf2"
-)
-
-var aesGcmNonceSize = 12
-
-func runHkdf(secret, salt, info []byte) ([]byte, error) {
- r := hkdf.New(sha256.New, secret, salt, info)
-
- ret := make([]byte, 32)
- _, err := io.ReadFull(r, ret)
- if err != nil {
- return []byte{}, errors.Wrap(err, "reading key bytes")
- }
-
- return ret, nil
-}
-
-// MakeKeys derives, from the given credential, a key set comprising of an encryption key
-// and an authentication key
-func MakeKeys(password, email []byte, iteration int) ([]byte, []byte, error) {
- masterKey := pbkdf2.Key([]byte(password), []byte(email), iteration, 32, sha256.New)
-
- authKey, err := runHkdf(masterKey, email, []byte("auth"))
- if err != nil {
- return nil, nil, errors.Wrap(err, "deriving auth key")
- }
-
- return masterKey, authKey, nil
-}
-
-// AesGcmEncrypt encrypts the plaintext using AES in a GCM mode. It returns
-// a ciphertext prepended by a 12 byte pseudo-random nonce, encoded in base64.
-func AesGcmEncrypt(key, plaintext []byte) (string, error) {
- if key == nil {
- return "", errors.New("no key provided")
- }
-
- block, err := aes.NewCipher(key)
- if err != nil {
- return "", errors.Wrap(err, "initializing aes")
- }
-
- aesgcm, err := cipher.NewGCM(block)
- if err != nil {
- return "", errors.Wrap(err, "initializing gcm")
- }
-
- nonce := make([]byte, aesGcmNonceSize)
- if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
- return "", errors.Wrap(err, "generating nonce")
- }
-
- ciphertext := aesgcm.Seal(nonce, nonce, []byte(plaintext), nil)
- cipherKeyB64 := base64.StdEncoding.EncodeToString(ciphertext)
-
- return cipherKeyB64, nil
-}
-
-// AesGcmDecrypt decrypts the encrypted data using AES in a GCM mode. The data should be
-// a base64 encoded string in the format of 12 byte nonce followed by a ciphertext.
-func AesGcmDecrypt(key []byte, dataB64 string) ([]byte, error) {
- if key == nil {
- return nil, errors.New("no key provided")
- }
-
- data, err := base64.StdEncoding.DecodeString(dataB64)
- if err != nil {
- return nil, errors.Wrap(err, "decoding base64 data")
- }
-
- block, err := aes.NewCipher(key)
- if err != nil {
- return nil, errors.Wrap(err, "initializing aes")
- }
-
- aesgcm, err := cipher.NewGCM(block)
- if err != nil {
- return nil, errors.Wrap(err, "initializing gcm")
- }
-
- if len(data) < aesGcmNonceSize {
- return nil, errors.Wrap(err, "malformed data")
- }
-
- nonce, ciphertext := data[:aesGcmNonceSize], data[aesGcmNonceSize:]
- plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
- if err != nil {
- return nil, errors.Wrap(err, "decrypting")
- }
-
- return plaintext, nil
-}
diff --git a/pkg/cli/crypt/crypto_test.go b/pkg/cli/crypt/crypto_test.go
deleted file mode 100644
index 805e2724..00000000
--- a/pkg/cli/crypt/crypto_test.go
+++ /dev/null
@@ -1,118 +0,0 @@
-/* Copyright (C) 2019, 2020, 2021, 2022, 2023, 2024, 2025 Dnote contributors
- *
- * This file is part of Dnote.
- *
- * Dnote is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Dnote is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Dnote. If not, see .
- */
-
-package crypt
-
-import (
- "crypto/aes"
- "crypto/cipher"
- "encoding/base64"
- "fmt"
- "testing"
-
- "github.com/dnote/dnote/pkg/assert"
- "github.com/pkg/errors"
-)
-
-func TestAesGcmEncrypt(t *testing.T) {
- testCases := []struct {
- key []byte
- plaintext []byte
- }{
- {
- key: []byte("AES256Key-32Characters1234567890"),
- plaintext: []byte("foo bar baz quz"),
- },
- {
- key: []byte("AES256Key-32Charactersabcdefghij"),
- plaintext: []byte("1234 foo 5678 bar 7890 baz"),
- },
- }
-
- for _, tc := range testCases {
- t.Run(fmt.Sprintf("key %s plaintext %s", tc.key, tc.plaintext), func(t *testing.T) {
- // encrypt
- dataB64, err := AesGcmEncrypt(tc.key, tc.plaintext)
- if err != nil {
- t.Fatal(errors.Wrap(err, "performing encryption"))
- }
-
- // test that data can be decrypted
- data, err := base64.StdEncoding.DecodeString(dataB64)
- if err != nil {
- t.Fatal(errors.Wrap(err, "decoding data from base64"))
- }
-
- nonce, ciphertext := data[:12], data[12:]
-
- fmt.Println(string(data))
-
- block, err := aes.NewCipher([]byte(tc.key))
- if err != nil {
- t.Fatal(errors.Wrap(err, "initializing aes"))
- }
-
- aesgcm, err := cipher.NewGCM(block)
- if err != nil {
- t.Fatal(errors.Wrap(err, "initializing gcm"))
- }
-
- plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
- if err != nil {
- t.Fatal(errors.Wrap(err, "decode"))
- }
-
- assert.DeepEqual(t, plaintext, tc.plaintext, "plaintext mismatch")
- })
- }
-}
-
-func TestAesGcmDecrypt(t *testing.T) {
- testCases := []struct {
- key []byte
- ciphertextB64 string
- expectedPlaintext string
- }{
- {
- key: []byte("AES256Key-32Characters1234567890"),
- ciphertextB64: "M2ov9hWMQ52v1S/zigwX3bJt4cVCV02uiRm/grKqN/rZxNkJrD7vK4Ii0g==",
- expectedPlaintext: "foo bar baz quz",
- },
- {
- key: []byte("AES256Key-32Characters1234567890"),
- ciphertextB64: "M4csFKUIUbD1FBEzLgHjscoKgN0lhMGJ0n2nKWiCkE/qSKlRP7kS",
- expectedPlaintext: "foo\n1\nbar\n2",
- },
- {
- key: []byte("AES256Key-32Characters1234567890"),
- ciphertextB64: "pe/fnw73MR1clmVIlRSJ5gDwBdnPly/DF7DsR5dJVz4dHZlv0b10WzvJEGOCHZEr+Q==",
- expectedPlaintext: "föo\nbār\nbåz & qūz",
- },
- }
-
- for _, tc := range testCases {
- t.Run(fmt.Sprintf("key %s ciphertext %s", tc.key, tc.ciphertextB64), func(t *testing.T) {
- plaintext, err := AesGcmDecrypt(tc.key, tc.ciphertextB64)
- if err != nil {
- t.Fatal(errors.Wrap(err, "performing decryption"))
- }
-
- assert.DeepEqual(t, plaintext, []byte(tc.expectedPlaintext), "plaintext mismatch")
- })
- }
-}
diff --git a/pkg/server/app/books.go b/pkg/server/app/books.go
index c3b89ec0..a476a0c4 100644
--- a/pkg/server/app/books.go
+++ b/pkg/server/app/books.go
@@ -41,12 +41,11 @@ func (a *App) CreateBook(user database.User, name string) (database.Book, error)
}
book := database.Book{
- UUID: uuid,
- UserID: user.ID,
- Label: name,
- AddedOn: a.Clock.Now().UnixNano(),
- USN: nextUSN,
- Encrypted: false,
+ UUID: uuid,
+ UserID: user.ID,
+ Label: name,
+ AddedOn: a.Clock.Now().UnixNano(),
+ USN: nextUSN,
}
if err := tx.Create(&book).Error; err != nil {
tx.Rollback()
@@ -99,8 +98,6 @@ func (a *App) UpdateBook(tx *gorm.DB, user database.User, book database.Book, la
book.USN = nextUSN
book.EditedOn = a.Clock.Now().UnixNano()
book.Deleted = false
- // TODO: remove after all users have been migrated
- book.Encrypted = false
if err := tx.Save(&book).Error; err != nil {
return book, errors.Wrap(err, "updating the book")
diff --git a/pkg/server/app/notes.go b/pkg/server/app/notes.go
index 7773953d..fabe62be 100644
--- a/pkg/server/app/notes.go
+++ b/pkg/server/app/notes.go
@@ -59,16 +59,15 @@ func (a *App) CreateNote(user database.User, bookUUID, content string, addedOn *
}
note := database.Note{
- UUID: uuid,
- BookUUID: bookUUID,
- UserID: user.ID,
- AddedOn: noteAddedOn,
- EditedOn: noteEditedOn,
- USN: nextUSN,
- Body: content,
- Public: public,
- Encrypted: false,
- Client: client,
+ UUID: uuid,
+ BookUUID: bookUUID,
+ UserID: user.ID,
+ AddedOn: noteAddedOn,
+ EditedOn: noteEditedOn,
+ USN: nextUSN,
+ Body: content,
+ Public: public,
+ Client: client,
}
if err := tx.Create(¬e).Error; err != nil {
tx.Rollback()
@@ -134,8 +133,6 @@ func (a *App) UpdateNote(tx *gorm.DB, user database.User, note database.Note, p
note.USN = nextUSN
note.EditedOn = a.Clock.Now().UnixNano()
note.Deleted = false
- // TODO: remove after all users are migrated
- note.Encrypted = false
if err := tx.Save(¬e).Error; err != nil {
return note, pkgErrors.Wrap(err, "editing note")
@@ -180,13 +177,12 @@ func (a *App) GetUserNoteByUUID(userID int, uuid string) (*database.Note, error)
// GetNotesParams is params for finding notes
type GetNotesParams struct {
- Year int
- Month int
- Page int
- Books []string
- Search string
- Encrypted bool
- PerPage int
+ Year int
+ Month int
+ Page int
+ Books []string
+ Search string
+ PerPage int
}
type ftsParams struct {
@@ -215,14 +211,13 @@ notes.added_on,
notes.edited_on,
notes.usn,
notes.deleted,
-notes.encrypted,
` + bodyExpr)
}
func getNotesBaseQuery(db *gorm.DB, userID int, q GetNotesParams) *gorm.DB {
conn := db.Where(
- "notes.user_id = ? AND notes.deleted = ? AND notes.encrypted = ?",
- userID, false, q.Encrypted,
+ "notes.user_id = ? AND notes.deleted = ?",
+ userID, false,
)
if q.Search != "" {
diff --git a/pkg/server/app/notes_test.go b/pkg/server/app/notes_test.go
index 7813c54b..710b0dae 100644
--- a/pkg/server/app/notes_test.go
+++ b/pkg/server/app/notes_test.go
@@ -374,10 +374,9 @@ func TestGetNotes_FTSSearch(t *testing.T) {
// Search "baz"
result, err := a.GetNotes(user.ID, GetNotesParams{
- Search: "baz",
- Encrypted: false,
- Page: 1,
- PerPage: 30,
+ Search: "baz",
+ Page: 1,
+ PerPage: 30,
})
if err != nil {
t.Fatal(errors.Wrap(err, "getting notes with FTS search"))
@@ -390,10 +389,9 @@ func TestGetNotes_FTSSearch(t *testing.T) {
// Search for "running" - should return 1 note
result, err = a.GetNotes(user.ID, GetNotesParams{
- Search: "running",
- Encrypted: false,
- Page: 1,
- PerPage: 30,
+ Search: "running",
+ Page: 1,
+ PerPage: 30,
})
if err != nil {
t.Fatal(errors.Wrap(err, "getting notes with FTS search for review"))
@@ -405,10 +403,9 @@ func TestGetNotes_FTSSearch(t *testing.T) {
// Search for non-existent term - should return 0 notes
result, err = a.GetNotes(user.ID, GetNotesParams{
- Search: "nonexistent",
- Encrypted: false,
- Page: 1,
- PerPage: 30,
+ Search: "nonexistent",
+ Page: 1,
+ PerPage: 30,
})
if err != nil {
t.Fatal(errors.Wrap(err, "getting notes with FTS search for nonexistent"))
@@ -437,10 +434,9 @@ func TestGetNotes_FTSSearch_Snippet(t *testing.T) {
// Search for "keyword" in long note - should return snippet with "..."
result, err := a.GetNotes(user.ID, GetNotesParams{
- Search: "keyword",
- Encrypted: false,
- Page: 1,
- PerPage: 30,
+ Search: "keyword",
+ Page: 1,
+ PerPage: 30,
})
if err != nil {
t.Fatal(errors.Wrap(err, "getting notes with FTS search for keyword"))
@@ -472,10 +468,9 @@ func TestGetNotes_FTSSearch_ShortWord(t *testing.T) {
a.Clock = clock.NewMock()
result, err := a.GetNotes(user.ID, GetNotesParams{
- Search: "a",
- Encrypted: false,
- Page: 1,
- PerPage: 30,
+ Search: "a",
+ Page: 1,
+ PerPage: 30,
})
if err != nil {
t.Fatal(errors.Wrap(err, "getting notes with FTS search for 'a'"))
@@ -504,10 +499,9 @@ func TestGetNotes_All(t *testing.T) {
a.Clock = clock.NewMock()
result, err := a.GetNotes(user.ID, GetNotesParams{
- Search: "",
- Encrypted: false,
- Page: 1,
- PerPage: 30,
+ Search: "",
+ Page: 1,
+ PerPage: 30,
})
if err != nil {
t.Fatal(errors.Wrap(err, "getting notes with FTS search for 'a'"))
diff --git a/pkg/server/controllers/books.go b/pkg/server/controllers/books.go
index e2aa6de0..1b4f3810 100644
--- a/pkg/server/controllers/books.go
+++ b/pkg/server/controllers/books.go
@@ -56,22 +56,11 @@ func (b *Books) getBooks(r *http.Request) ([]database.Book, error) {
query := r.URL.Query()
name := query.Get("name")
- encryptedStr := query.Get("encrypted")
if name != "" {
part := fmt.Sprintf("%%%s%%", name)
conn = conn.Where("LOWER(label) LIKE ?", part)
}
- if encryptedStr != "" {
- var encrypted bool
- if encryptedStr == "true" {
- encrypted = true
- } else {
- encrypted = false
- }
-
- conn = conn.Where("encrypted = ?", encrypted)
- }
var books []database.Book
if err := conn.Find(&books).Error; err != nil {
diff --git a/pkg/server/controllers/notes.go b/pkg/server/controllers/notes.go
index a7434366..74bdd3f1 100644
--- a/pkg/server/controllers/notes.go
+++ b/pkg/server/controllers/notes.go
@@ -73,7 +73,6 @@ func parseGetNotesQuery(q url.Values) (app.GetNotesParams, error) {
yearStr := q.Get("year")
monthStr := q.Get("month")
books := q["book"]
- encryptedStr := q.Get("encrypted")
pageStr := q.Get("page")
page, err := parsePageQuery(q)
@@ -107,21 +106,13 @@ func parseGetNotesQuery(q url.Values) (app.GetNotesParams, error) {
month = m
}
- var encrypted bool
- if strings.ToLower(encryptedStr) == "true" {
- encrypted = true
- } else {
- encrypted = false
- }
-
ret := app.GetNotesParams{
- Year: year,
- Month: month,
- Page: page,
- Search: parseSearchQuery(q),
- Books: books,
- Encrypted: encrypted,
- PerPage: notesPerPage,
+ Year: year,
+ Month: month,
+ Page: page,
+ Search: parseSearchQuery(q),
+ Books: books,
+ PerPage: notesPerPage,
}
return ret, nil
diff --git a/pkg/server/database/models.go b/pkg/server/database/models.go
index 98571e10..50ef2006 100644
--- a/pkg/server/database/models.go
+++ b/pkg/server/database/models.go
@@ -40,7 +40,6 @@ type Book struct {
EditedOn int64 `json:"edited_on"`
USN int `json:"-" gorm:"index"`
Deleted bool `json:"-" gorm:"default:false"`
- Encrypted bool `json:"-" gorm:"default:false"`
}
// Note is a model for a note
@@ -57,7 +56,6 @@ type Note struct {
Public bool `json:"public" gorm:"default:false"`
USN int `json:"-" gorm:"index"`
Deleted bool `json:"-" gorm:"default:false"`
- Encrypted bool `json:"-" gorm:"default:false"`
Client string `gorm:"index"`
}