3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-10 22:19:31 +01:00

factor out confirmation codes into utils, change their format

This commit is contained in:
Shivaram Lingamneni 2020-02-22 22:32:19 -05:00
parent 490b3722bd
commit 85a536977c
5 changed files with 63 additions and 15 deletions

View File

@ -4,17 +4,15 @@
package irc package irc
import ( import (
"bytes"
"fmt" "fmt"
"hash/crc32"
"sort" "sort"
"strconv"
"strings" "strings"
"time" "time"
"github.com/goshuirc/irc-go/ircfmt" "github.com/goshuirc/irc-go/ircfmt"
"github.com/oragono/oragono/irc/modes" "github.com/oragono/oragono/irc/modes"
"github.com/oragono/oragono/irc/sno" "github.com/oragono/oragono/irc/sno"
"github.com/oragono/oragono/irc/utils"
) )
const chanservHelp = `ChanServ lets you register and manage channels.` const chanservHelp = `ChanServ lets you register and manage channels.`
@ -352,7 +350,7 @@ func csUnregisterHandler(server *Server, client *Client, command string, params
} }
info := channel.ExportRegistration(0) info := channel.ExportRegistration(0)
expectedCode := unregisterConfirmationCode(info.Name, info.RegisteredAt) expectedCode := utils.ConfirmationCode(info.Name, info.RegisteredAt)
if expectedCode != verificationCode { if expectedCode != verificationCode {
csNotice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this channel will remove all stored channel attributes.$b"))) csNotice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this channel will remove all stored channel attributes.$b")))
csNotice(rb, fmt.Sprintf(client.t("To confirm channel unregistration, type: /CS UNREGISTER %[1]s %[2]s"), channelKey, expectedCode)) csNotice(rb, fmt.Sprintf(client.t("To confirm channel unregistration, type: /CS UNREGISTER %[1]s %[2]s"), channelKey, expectedCode))
@ -363,14 +361,6 @@ func csUnregisterHandler(server *Server, client *Client, command string, params
csNotice(rb, fmt.Sprintf(client.t("Channel %s is now unregistered"), channelKey)) csNotice(rb, fmt.Sprintf(client.t("Channel %s is now unregistered"), channelKey))
} }
// deterministically generates a confirmation code for unregistering a channel / account
func unregisterConfirmationCode(name string, registeredAt time.Time) (code string) {
var codeInput bytes.Buffer
codeInput.WriteString(name)
codeInput.WriteString(strconv.FormatInt(registeredAt.Unix(), 16))
return strconv.Itoa(int(crc32.ChecksumIEEE(codeInput.Bytes())))
}
func csClearHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { func csClearHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
channel := server.channels.Get(params[0]) channel := server.channels.Get(params[0])
if channel == nil { if channel == nil {
@ -426,7 +416,7 @@ func csTransferHandler(server *Server, client *Client, command string, params []
return return
} }
if targetAccount.NameCasefolded != account { if targetAccount.NameCasefolded != account {
expectedCode := unregisterConfirmationCode(regInfo.Name, regInfo.RegisteredAt) expectedCode := utils.ConfirmationCode(regInfo.Name, regInfo.RegisteredAt)
codeValidated := 2 < len(params) && params[2] == expectedCode codeValidated := 2 < len(params) && params[2] == expectedCode
if !codeValidated { if !codeValidated {
csNotice(rb, ircfmt.Unescape(client.t("$bWarning: you are about to transfer control of your channel to another user.$b"))) csNotice(rb, ircfmt.Unescape(client.t("$bWarning: you are about to transfer control of your channel to another user.$b")))

View File

@ -810,7 +810,7 @@ func debugHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
rb.Notice(client.t("You must have rehash permissions in order to execute DEBUG CRASHSERVER")) rb.Notice(client.t("You must have rehash permissions in order to execute DEBUG CRASHSERVER"))
return false return false
} }
code := unregisterConfirmationCode(server.name, server.ctime) code := utils.ConfirmationCode(server.name, server.ctime)
if len(msg.Params) == 1 || msg.Params[1] != code { if len(msg.Params) == 1 || msg.Params[1] != code {
rb.Notice(fmt.Sprintf(client.t("To crash the server, issue the following command: /DEBUG CRASHSERVER %s"), code)) rb.Notice(fmt.Sprintf(client.t("To crash the server, issue the following command: /DEBUG CRASHSERVER %s"), code))
return false return false

View File

@ -732,7 +732,7 @@ func nsUnregisterHandler(server *Server, client *Client, command string, params
return return
} }
expectedCode := unregisterConfirmationCode(account.Name, account.RegisteredAt) expectedCode := utils.ConfirmationCode(account.Name, account.RegisteredAt)
if expectedCode != verificationCode { if expectedCode != verificationCode {
nsNotice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this account will remove its stored privileges.$b"))) nsNotice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this account will remove its stored privileges.$b")))
nsNotice(rb, fmt.Sprintf(client.t("To confirm account unregistration, type: /NS UNREGISTER %[1]s %[2]s"), cfname, expectedCode)) nsNotice(rb, fmt.Sprintf(client.t("To confirm account unregistration, type: /NS UNREGISTER %[1]s %[2]s"), cfname, expectedCode))

22
irc/utils/confirmation.go Normal file
View File

@ -0,0 +1,22 @@
// Copyright (c) 2020 Shivaram Lingamneni <slingamn@cs.stanford.edu>
// released under the MIT license
package utils
import (
"crypto/sha256"
"encoding/binary"
"time"
)
// Deterministically generates a confirmation code for some destructive activity;
// `name` is typically the name of the identity being destroyed (a channel being
// unregistered, or the server being crashed) and `createdAt` means a different
// value is required each time.
func ConfirmationCode(name string, createdAt time.Time) (code string) {
buf := make([]byte, len(name)+8)
binary.BigEndian.PutUint64(buf, uint64(createdAt.UnixNano()))
copy(buf[8:], name[:])
out := sha256.Sum256(buf)
return B32Encoder.EncodeToString(out[:3])
}

View File

@ -0,0 +1,36 @@
// Copyright (c) 2020 Shivaram Lingamneni <slingamn@cs.stanford.edu>
// released under the MIT license
package utils
import (
"testing"
"time"
)
func easyParse(timestamp string) time.Time {
result, err := time.Parse("2006-01-02 15:04:05Z", timestamp)
if err != nil {
panic(err)
}
return result
}
func TestConfirmation(t *testing.T) {
set := make(map[string]struct{})
set[ConfirmationCode("#darwin", easyParse("2006-01-01 00:00:00Z"))] = struct{}{}
set[ConfirmationCode("#darwin", easyParse("2006-01-02 00:00:00Z"))] = struct{}{}
set[ConfirmationCode("#xelpers", easyParse("2006-01-01 00:00:00Z"))] = struct{}{}
set[ConfirmationCode("#xelpers", easyParse("2006-01-02 00:00:00Z"))] = struct{}{}
if len(set) != 4 {
t.Error("confirmation codes are not unique")
}
for code := range set {
if len(code) <= 2 || len(code) >= 8 {
t.Errorf("bad code: %s", code)
}
}
}