diff --git a/src/cmd/coller/README.md b/src/cmd/coller/README.md index 1e3d178..1cf57a6 100644 --- a/src/cmd/coller/README.md +++ b/src/cmd/coller/README.md @@ -22,17 +22,17 @@ Create from file: coller -file filename.txt ``` -Provide encryption key: +Provide password for encryption: ``` -coller -ask-encryption-key -coller -encryption-key ENCRYPTION_KEY +coller -ask-password +coller -password PASSWORD ``` -Create a note in cleartext: +Create public note: ``` -coller -no-encryption +coller -no-password ``` Return the copier command to use client-side decryption instead of the URL: diff --git a/src/cmd/coller/main.go b/src/cmd/coller/main.go index 445ea14..67a7e59 100644 --- a/src/cmd/coller/main.go +++ b/src/cmd/coller/main.go @@ -64,10 +64,10 @@ func handleMain() int { configFile := flag.String("config", filepath.Join(homeDir, ".config", AppName+".json"), "Configuration file") reconfigure := flag.Bool("reconfigure", false, "Re-create configuration file") url := flag.String("url", "", "URL of the coller API") - encryptionKey := flag.String("encryption-key", os.Getenv("COLLER_ENCRYPTION_KEY"), "Key to encrypt the note") - askEncryptionKey := flag.Bool("ask-encryption-key", false, "Read encryption key from input") - noEncryption := flag.Bool("no-encryption", false, "Allow notes without encryption key") - encryptionKeyLength := flag.Int("encryption-key-length", 16, "Length of the auto-generated encryption key") + password := flag.String("password", os.Getenv("COLLER_PASSWORD"), "Password to encrypt the note") + askPassword := flag.Bool("ask-password", false, "Read password from input") + noPassword := flag.Bool("no-password", false, "Allow notes without password") + passwordLength := flag.Int("password-length", 16, "Length of the auto-generated password") flag.StringVar(&fileName, "file", "", "Read content of the note from a file") expiration := flag.Int("expiration", 0, "Number of seconds before expiration") deleteAfterRead := flag.Bool("delete-after-read", false, "Delete the note after the first read") @@ -140,22 +140,22 @@ func handleMain() int { content = clipboard.Read(clipboard.FmtText) } - if *askEncryptionKey { - fmt.Print("Encryption key: ") + if *askPassword { + fmt.Print("Password: ") p, err := term.ReadPassword(int(syscall.Stdin)) if err != nil { - return internal.ReturnError(logger, "could not read encryption key", err) + return internal.ReturnError(logger, "could not read password", err) } - *encryptionKey = string(p) + *password = string(p) fmt.Print("\n") } - if !*noEncryption && *encryptionKey == "" { - logger.Debug("generating random encryption key") - if *encryptionKeyLength < internal.MIN_ENCRYPTION_KEY_LENGTH || *encryptionKeyLength > internal.MAX_ENCRYPTION_KEY_LENGTH { - return internal.ReturnError(logger, "invalid length of auto-generated encryption key", fmt.Errorf("encryption key length must be between %d and %d", internal.MIN_ENCRYPTION_KEY_LENGTH, internal.MAX_ENCRYPTION_KEY_LENGTH)) + if !*noPassword && *password == "" { + logger.Debug("generating random password") + if *passwordLength < internal.MIN_PASSWORD_LENGTH || *passwordLength > internal.MAX_PASSWORD_LENGTH { + return internal.ReturnError(logger, "invalid password length for auto-generated password", fmt.Errorf("password length must be between %d and %d", internal.MIN_PASSWORD_LENGTH, internal.MAX_PASSWORD_LENGTH)) } - *encryptionKey = internal.GenerateChars(*encryptionKeyLength) + *password = internal.GenerateChars(*passwordLength) } if len(content) == 0 { @@ -173,13 +173,13 @@ func handleMain() int { p.Language = *language } - if *encryptionKey != "" { - logger.Debug("validating encryption key") - if err = internal.ValidateEncryptionKey(*encryptionKey); err != nil { - return internal.ReturnError(logger, "invalid encryption key", nil) + if *password != "" { + logger.Debug("validating password") + if err = internal.ValidatePassword(*password); err != nil { + return internal.ReturnError(logger, "invalid password", nil) } logger.Debug("encrypting content") - content, err = internal.Encrypt(content, *encryptionKey) + content, err = internal.Encrypt(content, *password) if err != nil { return internal.ReturnError(logger, "could not encrypt note", err) } @@ -242,21 +242,21 @@ func handleMain() int { logger.Debug("finding note location") var location string noteURL := *url + "/" + jsonBody.ID - if *encryptionKey != "" { + if *password != "" { if *copier { - location = fmt.Sprintf("copier -encryption-key %s %s", *encryptionKey, noteURL) + location = fmt.Sprintf("copier -password %s %s", *password, noteURL) } else { if *html { - location = fmt.Sprintf("%s/%s.html", noteURL, *encryptionKey) + location = fmt.Sprintf("%s/%s.html", noteURL, *password) } else { - location = fmt.Sprintf("%s/%s", noteURL, *encryptionKey) + location = fmt.Sprintf("%s/%s", noteURL, *password) } } } else { if *html { location = fmt.Sprintf("%s.html", noteURL) } else { - location = noteURL + location = fmt.Sprintf("%s", noteURL) } } diff --git a/src/cmd/collerd/README.md b/src/cmd/collerd/README.md index 5431de8..01fc7b0 100644 --- a/src/cmd/collerd/README.md +++ b/src/cmd/collerd/README.md @@ -19,7 +19,7 @@ The file format is **JSON**: * **database_type** (string): Type of the database (default "sqlite", "postgres" also supported) * **database_dsn** (string): Connection string for the database (default "collerd.db") * **node_id** (int): Number between 0 and 1023 to define the node generating identifiers (see [snowflake](https://github.com/bwmarrin/snowflake)) -* **encryption_key_length** (int): Number of characters for generated encryption key (default 16) +* **password_length** (int): Number of characters for generated passwords (default 16) * **expiration_interval** (int): Number of seconds to wait between two expiration runs * **listen_address** (string): Address to listen for the web server (default "0.0.0.0") * **listen_port** (int): Port to listen for the web server (default 8080) @@ -54,7 +54,7 @@ Create a note. Body (JSON): * **content** (string): base64 encoded content (required) -* **encryption_key** (string): use server-side encryption with this encryption key +* **password** (string): use server-side encryption with this password * **encrypted** (bool): true if the content has been encrypted by the client * **expiration** (int): lifetime of the note in seconds (must be supported by the server) * **delete_after_read** (bool): delete the note after the first read @@ -64,12 +64,12 @@ Response (JSON): * **id** (string): ID of the note -### GET /\/\ +### GET /\/\ > [!WARNING] -> Potential encryption key leak +> Potential password leak -Return content of a note encrypted by the given encryption key. +Return content of a note encrypted by the given password. ### GET /\ diff --git a/src/cmd/collerd/main.go b/src/cmd/collerd/main.go index 265fe73..4e45b1f 100644 --- a/src/cmd/collerd/main.go +++ b/src/cmd/collerd/main.go @@ -70,7 +70,7 @@ func handleMain() int { return internal.ReturnError(logger, "could not create server", err) } - srv.SetEncryptionKeyLength(config.EncryptionKeyLength) + srv.SetPasswordLength(config.PasswordLength) if config.EnableMetrics { reg := prometheus.NewRegistry() diff --git a/src/cmd/copier/README.md b/src/cmd/copier/README.md index ed268e0..81a7a88 100644 --- a/src/cmd/copier/README.md +++ b/src/cmd/copier/README.md @@ -11,6 +11,6 @@ copier -help # Examples ``` -copier -encryption-key ENCRYPTION_KEY URL -copier -ask-encryption-key URL +copier -password PASSWORD URL +copier -ask-password URL ``` \ No newline at end of file diff --git a/src/cmd/copier/main.go b/src/cmd/copier/main.go index 5924936..73c18d3 100644 --- a/src/cmd/copier/main.go +++ b/src/cmd/copier/main.go @@ -9,9 +9,8 @@ import ( "os" "syscall" - "golang.org/x/term" - "git.riou.xyz/jriou/coller/internal" + "golang.org/x/term" ) var ( @@ -29,8 +28,8 @@ func handleMain() int { quiet := flag.Bool("quiet", false, "Log errors only") verbose := flag.Bool("verbose", false, "Print more logs") debug := flag.Bool("debug", false, "Print even more logs") - encryptionKey := flag.String("encryption-key", os.Getenv("COLLER_ENCRYPTION_KEY"), "Key to decrypt the note") - askEncryptionKey := flag.Bool("ask-encryption-key", false, "Read encryption key from input") + password := flag.String("password", os.Getenv("COLLER_PASSWORD"), "Password to decrypt the note") + askPassword := flag.Bool("ask-password", false, "Read password from input") fileName := flag.String("file", "", "Write content of the note to a file") bearer := flag.String("bearer", os.Getenv("COLLER_BEARER"), "Bearer token") askBearer := flag.Bool("ask-bearer", false, "Read bearer token from input") @@ -61,13 +60,13 @@ func handleMain() int { } logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: level})) - if *askEncryptionKey { - fmt.Print("Encryption key: ") + if *askPassword { + fmt.Print("Password: ") p, err := term.ReadPassword(int(syscall.Stdin)) if err != nil { - return internal.ReturnError(logger, "could not read encryption key", err) + return internal.ReturnError(logger, "could not read password", err) } - *encryptionKey = string(p) + *password = string(p) fmt.Print("\n") } @@ -103,11 +102,11 @@ func handleMain() int { } var content []byte - if *encryptionKey != "" { + if *password != "" { logger.Debug("decrypting note") - content, err = internal.Decrypt(body, *encryptionKey) + content, err = internal.Decrypt(body, *password) if err != nil { - return internal.ReturnError(logger, "could not decrypt note", err) + return internal.ReturnError(logger, "could not decrypt paste", err) } } else { content = body diff --git a/src/internal/encryption.go b/src/internal/encryption.go index af8446b..be605bf 100644 --- a/src/internal/encryption.go +++ b/src/internal/encryption.go @@ -19,21 +19,21 @@ const ( // NewCipher creates a cipher using XChaCha20-Poly1305 // https://pkg.go.dev/golang.org/x/crypto/chacha20poly1305 -// A salt is required to derive the key from an encryption key using argon -func NewCipher(encryptionKey string, salt []byte) (cipher.AEAD, error) { - key := argon2.IDKey([]byte(encryptionKey), salt, KeyTime, KeyMemory, KeyThreads, KeySize) +// A salt is required to derive the key from a password using argon +func NewCipher(password string, salt []byte) (cipher.AEAD, error) { + key := argon2.IDKey([]byte(password), salt, KeyTime, KeyMemory, KeyThreads, KeySize) return chacha20poly1305.NewX(key) } -// Encrypt to encrypt a plaintext with an encryption key +// Encrypt to encrypt a plaintext with a password // Returns a byte slice with the generated salt, nonce and the ciphertext -func Encrypt(plaintext []byte, encryptionKey string) (result []byte, err error) { +func Encrypt(plaintext []byte, password string) (result []byte, err error) { salt := make([]byte, SaltSize) if n, err := rand.Read(salt); err != nil || n != SaltSize { return nil, err } - aead, err := NewCipher(encryptionKey, salt) + aead, err := NewCipher(password, salt) if err != nil { return nil, err } @@ -53,15 +53,15 @@ func Encrypt(plaintext []byte, encryptionKey string) (result []byte, err error) return result, nil } -// Decrypt to decrypt a ciphertext with a encryption key +// Decrypt to decrypt a ciphertext with a password // Returns the plaintext -func Decrypt(ciphertext []byte, encryptionKey string) ([]byte, error) { +func Decrypt(ciphertext []byte, password string) ([]byte, error) { if len(ciphertext) < SaltSize { return nil, fmt.Errorf("ciphertext is too short: cannot read salt") } salt := ciphertext[:SaltSize] - aead, err := NewCipher(encryptionKey, salt) + aead, err := NewCipher(password, salt) if err != nil { return nil, err } diff --git a/src/internal/encryption_test.go b/src/internal/encryption_test.go index 6ff44d4..14b5f6a 100644 --- a/src/internal/encryption_test.go +++ b/src/internal/encryption_test.go @@ -6,10 +6,10 @@ import ( func TestEncryptAndDecrypt(t *testing.T) { plaintext := "test" - encryptionKey := "test" - wrongEncryptionKey := encryptionKey + "wrong" + password := "test" + wrongPassword := password + "wrong" - ciphertext, err := Encrypt([]byte(plaintext), encryptionKey) + ciphertext, err := Encrypt([]byte(plaintext), password) if err != nil { t.Errorf("unexpected error when encrypting: %v", err) return @@ -20,7 +20,7 @@ func TestEncryptAndDecrypt(t *testing.T) { return } - cleartext, err := Decrypt(ciphertext, encryptionKey) + cleartext, err := Decrypt(ciphertext, password) if err != nil { t.Errorf("unexpected error when decrypting: %v", err) return @@ -31,14 +31,14 @@ func TestEncryptAndDecrypt(t *testing.T) { return } - if encryptionKey == wrongEncryptionKey { - t.Errorf("encryption keys must be different") + if password == wrongPassword { + t.Errorf("passwords must be different") return } - _, err = Decrypt(ciphertext, wrongEncryptionKey) + _, err = Decrypt(ciphertext, wrongPassword) if err == nil { - t.Errorf("expected error when decrypting with a wrong encryption key, got none") + t.Errorf("expected error when decrypting with a wrong password, got none") return } } diff --git a/src/internal/internal.go b/src/internal/internal.go index 9141158..2780ae9 100644 --- a/src/internal/internal.go +++ b/src/internal/internal.go @@ -1,8 +1,8 @@ package internal const ( - RC_OK = 0 - RC_ERROR = 1 - MIN_ENCRYPTION_KEY_LENGTH = 16 - MAX_ENCRYPTION_KEY_LENGTH = 256 + RC_OK = 0 + RC_ERROR = 1 + MIN_PASSWORD_LENGTH = 16 + MAX_PASSWORD_LENGTH = 256 ) diff --git a/src/internal/utils.go b/src/internal/utils.go index 601f001..a1aa6fc 100644 --- a/src/internal/utils.go +++ b/src/internal/utils.go @@ -58,13 +58,13 @@ func GenerateChars(n int) string { return string(b) } -// Encryption key must be URL compatible and strong enough +// Passwords must be URL compatible and strong enough // Requiring only alphanumeric chars with a size between 16 and 256 -var encryptionKeyRegexp = regexp.MustCompile("^[a-zA-Z0-9]{16,256}$") +var passwordRegexp = regexp.MustCompile("^[a-zA-Z0-9]{16,256}$") -func ValidateEncryptionKey(p string) error { - if !encryptionKeyRegexp.MatchString(p) { - return fmt.Errorf("encryption key doesn't match '%s'", encryptionKeyRegexp) +func ValidatePassword(p string) error { + if !passwordRegexp.MatchString(p) { + return fmt.Errorf("password doesn't match '%s'", passwordRegexp) } return nil } diff --git a/src/server/config.go b/src/server/config.go index ca614ee..256104f 100644 --- a/src/server/config.go +++ b/src/server/config.go @@ -11,7 +11,7 @@ type Config struct { DatabaseType string `json:"database_type"` DatabaseDsn string `json:"database_dsn"` NodeID int64 `json:"node_id"` - EncryptionKeyLength int `json:"encryption_key_length"` + PasswordLength int `json:"password_length"` ExpirationInterval int `json:"expiration_interval"` ListenAddress string `json:"listen_address"` ListenPort int `json:"listen_port"` @@ -35,14 +35,14 @@ type Config struct { func NewConfig() *Config { return &Config{ - Title: "Coller", - DatabaseType: "sqlite", - DatabaseDsn: "collerd.db", - NodeID: 1, - EncryptionKeyLength: 16, - ExpirationInterval: 60, // 1 minute - ListenAddress: "0.0.0.0", - ListenPort: 8080, + Title: "Coller", + DatabaseType: "sqlite", + DatabaseDsn: "collerd.db", + NodeID: 1, + PasswordLength: 16, + ExpirationInterval: 60, // 1 minute + ListenAddress: "0.0.0.0", + ListenPort: 8080, Expirations: []int{ 300, // 5 minutes 3600, // 1 hour @@ -93,8 +93,8 @@ func (c *Config) Check() error { return fmt.Errorf("node id must be between 0 and 1023") } - if c.EncryptionKeyLength < internal.MIN_ENCRYPTION_KEY_LENGTH || c.EncryptionKeyLength > internal.MAX_ENCRYPTION_KEY_LENGTH { - return fmt.Errorf("encryption key length must be between %d and %d", internal.MIN_ENCRYPTION_KEY_LENGTH, internal.MAX_ENCRYPTION_KEY_LENGTH) + if c.PasswordLength < internal.MIN_PASSWORD_LENGTH || c.PasswordLength > internal.MAX_PASSWORD_LENGTH { + return fmt.Errorf("password length must be between %d and %d", internal.MIN_PASSWORD_LENGTH, internal.MAX_PASSWORD_LENGTH) } return nil } diff --git a/src/server/db.go b/src/server/db.go index 58982c9..e158618 100644 --- a/src/server/db.go +++ b/src/server/db.go @@ -122,7 +122,7 @@ func (d *Database) Get(id string) (*Note, error) { return nil, nil } -func (d *Database) Create(content []byte, encryptionKey string, encrypted bool, expiration int, deleteAfterRead bool, language string) (note *Note, err error) { +func (d *Database) Create(content []byte, password string, encrypted bool, expiration int, deleteAfterRead bool, language string) (note *Note, err error) { if expiration == 0 { expiration = d.expiration } @@ -148,11 +148,11 @@ func (d *Database) Create(content []byte, encryptionKey string, encrypted bool, DeleteAfterRead: deleteAfterRead, Language: language, } - if encryptionKey != "" { - if err = internal.ValidateEncryptionKey(encryptionKey); err != nil { + if password != "" { + if err = internal.ValidatePassword(password); err != nil { return nil, err } - note.Content, err = internal.Encrypt(note.Content, encryptionKey) + note.Content, err = internal.Encrypt(note.Content, password) if err != nil { return nil, err } diff --git a/src/server/handlers_api.go b/src/server/handlers_api.go index feab64e..f260328 100644 --- a/src/server/handlers_api.go +++ b/src/server/handlers_api.go @@ -23,7 +23,7 @@ type CreateNoteHandler struct { type CreateNotePayload struct { Content string `json:"content"` - EncryptionKey string `json:"encryption_key"` + Password string `json:"password"` Encrypted bool `json:"encrypted"` Expiration int `json:"expiration"` DeleteAfterRead bool `json:"delete_after_read"` @@ -54,7 +54,7 @@ func (h *CreateNoteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - note, err := h.db.Create(content, body.EncryptionKey, body.Encrypted, body.Expiration, body.DeleteAfterRead, body.Language) + note, err := h.db.Create(content, body.Password, body.Encrypted, body.Expiration, body.DeleteAfterRead, body.Language) if err != nil { WriteError(w, "could not create note", err) return @@ -99,7 +99,7 @@ func (h *GetProtectedNoteHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque vars := mux.Vars(r) id := vars["id"] - encryptionKey := vars["encryptionKey"] + password := vars["password"] note, err := h.db.Get(id) @@ -111,8 +111,8 @@ func (h *GetProtectedNoteHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque return } - if encryptionKey != "" && note.Encrypted { - note.Content, err = internal.Decrypt(note.Content, encryptionKey) + if password != "" && note.Encrypted { + note.Content, err = internal.Decrypt(note.Content, password) if err != nil { WriteError(w, "could not decrypt note", err) return diff --git a/src/server/handlers_web.go b/src/server/handlers_web.go index 733dbc9..2eb9bea 100644 --- a/src/server/handlers_web.go +++ b/src/server/handlers_web.go @@ -112,15 +112,15 @@ func (h *CreateNoteWithFormHandler) ServeHTTP(w http.ResponseWriter, r *http.Req } h.logger.Debug("checking inputs") - noEncryption := r.FormValue("no-encryption") - encryptionKey := r.FormValue("encryption-key") + noPassword := r.FormValue("no-password") + password := r.FormValue("password") expiration := r.FormValue("expiration") deleteAfterRead := r.FormValue("delete-after-read") language := r.FormValue("language") - if encryptionKey == "" && noEncryption == "" { - h.logger.Debug("generating encryption key") - encryptionKey = internal.GenerateChars(encryptionKeyLength) + if password == "" && noPassword == "" { + h.logger.Debug("generating password") + password = internal.GenerateChars(passwordLength) } h.logger.Debug("computing expiration") @@ -132,7 +132,7 @@ func (h *CreateNoteWithFormHandler) ServeHTTP(w http.ResponseWriter, r *http.Req } h.logger.Debug("saving note to the database") - note, err := h.db.Create(content, encryptionKey, encryptionKey != "", expirationInt, deleteAfterRead != "", language) + note, err := h.db.Create(content, password, password != "", expirationInt, deleteAfterRead != "", language) if err != nil { h.PageData.Err = err h.Templates.ExecuteTemplate(w, templateName, h.PageData) @@ -146,8 +146,8 @@ func (h *CreateNoteWithFormHandler) ServeHTTP(w http.ResponseWriter, r *http.Req } h.PageData.URL = fmt.Sprintf("%s%s/%s", scheme, r.Host, note.ID) - if encryptionKey != "" { - h.PageData.URL += "/" + encryptionKey + if password != "" { + h.PageData.URL += "/" + password } h.logger.Debug("rendering page") @@ -200,7 +200,7 @@ func (h *GetProtectedWebNoteHandler) ServeHTTP(w http.ResponseWriter, r *http.Re vars := mux.Vars(r) id := vars["id"] - encryptionKey := vars["encryptionKey"] + password := vars["password"] note, err := h.db.Get(id) @@ -216,8 +216,8 @@ func (h *GetProtectedWebNoteHandler) ServeHTTP(w http.ResponseWriter, r *http.Re return } - if encryptionKey != "" && note.Encrypted { - note.Content, err = internal.Decrypt(note.Content, encryptionKey) + if password != "" && note.Encrypted { + note.Content, err = internal.Decrypt(note.Content, password) if err != nil { h.PageData.Err = fmt.Errorf("could not decrypt note: %v", err) h.Templates.ExecuteTemplate(w, templateName, h.PageData) diff --git a/src/server/server.go b/src/server/server.go index 9066e16..165edab 100644 --- a/src/server/server.go +++ b/src/server/server.go @@ -16,10 +16,10 @@ import ( ) var ( - encryptionKeyLength = internal.MIN_ENCRYPTION_KEY_LENGTH - supportedOSes = []string{"linux", "darwin"} - supportedArches = []string{"amd64", "arm64"} - supportedClients = []string{"coller", "copier"} + passwordLength = internal.MIN_PASSWORD_LENGTH + supportedOSes = []string{"linux", "darwin"} + supportedArches = []string{"amd64", "arm64"} + supportedClients = []string{"coller", "copier"} ) type Server struct { @@ -41,8 +41,8 @@ func NewServer(logger *slog.Logger, db *Database, config *Config, version string }, nil } -func (s *Server) SetEncryptionKeyLength(length int) { - encryptionKeyLength = length +func (s *Server) SetPasswordLength(length int) { + passwordLength = length } func (s *Server) SetMetrics(metrics *Metrics) { @@ -100,7 +100,7 @@ func (s *Server) Start() error { // API r.Path("/api/note").Handler(&CreateNoteHandler{logger: s.logger, db: s.db, maxUploadSize: s.config.MaxUploadSize}).Methods("POST") - r.Path("/{id:[a-zA-Z0-9]+}/{encryptionKey:[a-zA-Z0-9]+}").Handler(&GetProtectedNoteHandler{logger: s.logger, db: s.db}).Methods("GET") + r.Path("/{id:[a-zA-Z0-9]+}/{password:[a-zA-Z0-9]+}").Handler(&GetProtectedNoteHandler{logger: s.logger, db: s.db}).Methods("GET") r.Path("/{id:[a-zA-Z0-9]+}").Handler(&GetNoteHandler{logger: s.logger, db: s.db}).Methods("GET") // Web pages @@ -153,7 +153,7 @@ func (s *Server) Start() error { logger: s.logger, db: s.db, } - r.Path("/{id:[a-zA-Z0-9]+}/{encryptionKey:[a-zA-Z0-9]+}.html").Handler(protectedWebNoteHandler).Methods("GET") + r.Path("/{id:[a-zA-Z0-9]+}/{password:[a-zA-Z0-9]+}.html").Handler(protectedWebNoteHandler).Methods("GET") webNoteHandler := &GetWebNoteHandler{ Templates: templates, diff --git a/src/server/templates/index.html b/src/server/templates/index.html index 2ab0df4..9974972 100644 --- a/src/server/templates/index.html +++ b/src/server/templates/index.html @@ -14,17 +14,17 @@
- +
+ title="Letters and numbers with length from 16 to 256" class="form-control" id="password" + name="password">
- - + +