Add channel modes +i, +m and +r, MOTD config option and banned message when connecting
parent
7db983d592
commit
e91d8a4dae
|
@ -23,7 +23,7 @@
|
|||
branch = "master"
|
||||
name = "github.com/jmoiron/sqlx"
|
||||
packages = [".","reflectx"]
|
||||
revision = "99f3ad6d85ae53d0fecf788ab62d0e9734b3c117"
|
||||
revision = "de8647470aafe4854c976707c431dbe1eb2822c6"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-sqlite3"
|
||||
|
@ -41,13 +41,13 @@
|
|||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["sha3"]
|
||||
revision = "94eea52f7b742c7cbe0b03b22f0c4c8631ece122"
|
||||
revision = "d585fd2cc9195196078f516b69daff6744ef5e84"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = ["context"]
|
||||
revision = "dc871a5d77e227f5bbf6545176ef3eeebf87e76e"
|
||||
revision = "d866cfc389cec985d6fda2859936a575a55a3ab6"
|
||||
|
||||
[[projects]]
|
||||
branch = "v2"
|
||||
|
|
|
@ -13,7 +13,7 @@ const ENTITY_STATE_TERMINATING = 0
|
|||
const ENTITY_STATE_NORMAL = 1
|
||||
|
||||
const CLIENT_MODES = "cD"
|
||||
const CHANNEL_MODES = "cDipstz"
|
||||
const CHANNEL_MODES = "cDimprstz"
|
||||
const CHANNEL_MODES_ARG = "kl"
|
||||
|
||||
type Entity struct {
|
||||
|
|
2
main.go
2
main.go
|
@ -35,7 +35,7 @@ import (
|
|||
var prefixAnonIRC = irc.Prefix{Name: "AnonIRC"}
|
||||
var prefixAnonymous = irc.Prefix{Name: "Anonymous", User: "Anon", Host: "IRC"}
|
||||
|
||||
const motd = `
|
||||
const DEFAULT_MOTD = `
|
||||
_|_| _|_|_| _|_|_| _|_|_|
|
||||
_| _| _|_|_| _|_| _|_|_| _| _| _| _|
|
||||
_|_|_|_| _| _| _| _| _| _| _| _|_|_| _|
|
||||
|
|
428
server.go
428
server.go
|
@ -33,6 +33,9 @@ const (
|
|||
COMMAND_USERNAME = "USERNAME"
|
||||
COMMAND_PASSWORD = "PASSWORD"
|
||||
|
||||
// User/channel commands
|
||||
COMMAND_MODE = "MODE"
|
||||
|
||||
// Channel/server commands
|
||||
COMMAND_FOUND = "FOUND"
|
||||
COMMAND_DROP = "DROP"
|
||||
|
@ -42,7 +45,7 @@ const (
|
|||
COMMAND_BAN = "BAN"
|
||||
COMMAND_AUDIT = "AUDIT"
|
||||
|
||||
// Server admins only
|
||||
// Server admin commands
|
||||
COMMAND_KILL = "KILL"
|
||||
COMMAND_STATS = "STATS"
|
||||
COMMAND_REHASH = "REHASH"
|
||||
|
@ -51,6 +54,7 @@ const (
|
|||
|
||||
var serverCommands = []string{COMMAND_KILL, COMMAND_STATS, COMMAND_REHASH, COMMAND_UPGRADE}
|
||||
|
||||
// TODO: Reorder
|
||||
const (
|
||||
PERMISSION_CLIENT = 0
|
||||
PERMISSION_REGISTERED = 1
|
||||
|
@ -69,11 +73,11 @@ var permissionLabels = map[int]string{
|
|||
PERMISSION_SUPERADMIN: "Super Administrator",
|
||||
}
|
||||
|
||||
var ALL_PERMISSIONS = "Client, Registered Client, VIP, Moderator, Adminsitrator and Super Administrator"
|
||||
var ALL_PERMISSIONS = "Client, Registered Client, VIP, Moderator, Administrator and Super Administrator"
|
||||
|
||||
var commandRestrictions = map[int][]string{
|
||||
PERMISSION_REGISTERED: {COMMAND_TOKEN, COMMAND_USERNAME, COMMAND_PASSWORD, COMMAND_FOUND},
|
||||
PERMISSION_MODERATOR: {COMMAND_REVEAL, COMMAND_KICK, COMMAND_BAN},
|
||||
PERMISSION_MODERATOR: {COMMAND_MODE, COMMAND_REVEAL, COMMAND_KICK, COMMAND_BAN},
|
||||
PERMISSION_ADMIN: {COMMAND_GRANT, COMMAND_AUDIT},
|
||||
PERMISSION_SUPERADMIN: {COMMAND_DROP, COMMAND_KILL, COMMAND_STATS, COMMAND_REHASH, COMMAND_UPGRADE}}
|
||||
|
||||
|
@ -81,7 +85,7 @@ var helpDuration = "Duration can be 0 to never expire, or e.g. 30m, 1h, 2d, 3w"
|
|||
var commandUsage = map[string][]string{
|
||||
COMMAND_HELP: {"[command|all]",
|
||||
"Print usage information regarding a specific command or 'all'",
|
||||
"Without a command or 'all', only commands currently available are printed"},
|
||||
"Without a command or 'all', only commands you have permission to use are printed"},
|
||||
COMMAND_INFO: {"[channel]",
|
||||
"When a channel is specified, prints info including whether it is registered",
|
||||
"Without a channel, server info is printed"},
|
||||
|
@ -135,6 +139,7 @@ var commandUsage = map[string][]string{
|
|||
}
|
||||
|
||||
type Config struct {
|
||||
MOTD string
|
||||
Salt string
|
||||
DBDriver string
|
||||
DBSource string
|
||||
|
@ -146,6 +151,7 @@ type Server struct {
|
|||
config *Config
|
||||
configfile string
|
||||
created int64
|
||||
motd []string
|
||||
clients *sync.Map
|
||||
channels *sync.Map
|
||||
odyssey *os.File
|
||||
|
@ -314,6 +320,54 @@ func (s *Server) inChannel(channel string, client string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (s *Server) canAccessChannel(c *Client, channel string) (bool, string) {
|
||||
dbch, err := db.Channel(channel)
|
||||
if err != nil || dbch.Channel == "" {
|
||||
return false, "invalid channel"
|
||||
}
|
||||
ch := s.getChannel(channel)
|
||||
if ch == nil {
|
||||
return false, "invalid channel"
|
||||
} else if banned, reason := c.isBanned(channel); banned {
|
||||
if reason != "" {
|
||||
reason = fmt.Sprintf(" (%s)", reason)
|
||||
}
|
||||
return false, "you are banned" + reason
|
||||
} else if ch.hasMode("z") && !c.ssl {
|
||||
return false, "only clients connected via SSL are allowed"
|
||||
}
|
||||
|
||||
permission := c.globalPermission()
|
||||
requiredPermission := PERMISSION_CLIENT
|
||||
reason := ""
|
||||
|
||||
if channel[0] == '&' {
|
||||
if permission < PERMISSION_VIP {
|
||||
return false, "restricted channel"
|
||||
}
|
||||
} else if channel[0] != '#' {
|
||||
return false, "invalid channel"
|
||||
}
|
||||
|
||||
if permission < requiredPermission && c.account > 0 {
|
||||
chp, err := db.GetPermission(c.account, channel)
|
||||
if err != nil && chp.Permission > permission {
|
||||
permission = chp.Permission
|
||||
}
|
||||
}
|
||||
|
||||
if ch.hasMode("r") {
|
||||
requiredPermission = PERMISSION_REGISTERED
|
||||
reason = "only registered clients are allowed"
|
||||
}
|
||||
if ch.hasMode("i") {
|
||||
requiredPermission = PERMISSION_VIP
|
||||
reason = "only VIP are allowed"
|
||||
}
|
||||
|
||||
return permission >= requiredPermission, reason
|
||||
}
|
||||
|
||||
func (s *Server) joinChannel(channel string, client string) {
|
||||
if s.inChannel(channel, client) {
|
||||
return // Already in channel
|
||||
|
@ -324,33 +378,14 @@ func (s *Server) joinChannel(channel string, client string) {
|
|||
return
|
||||
}
|
||||
|
||||
if len(channel) == 0 {
|
||||
return
|
||||
} else if channel[0] == '&' {
|
||||
if cl.globalPermission() < PERMISSION_VIP {
|
||||
cl.accessDenied(0)
|
||||
return
|
||||
}
|
||||
} else if channel[0] != '#' {
|
||||
return
|
||||
}
|
||||
|
||||
ch := s.getChannel(channel)
|
||||
if ch == nil {
|
||||
ch = NewChannel(channel)
|
||||
s.channels.Store(channel, ch)
|
||||
} else if ch.hasMode("z") && !cl.ssl {
|
||||
cl.sendNotice("Unable to join " + channel + ": SSL connections only (channel mode +z)")
|
||||
return
|
||||
}
|
||||
|
||||
banned, reason := cl.isBanned(channel)
|
||||
if banned {
|
||||
ex := ""
|
||||
if reason != "" {
|
||||
ex = ". Reason: " + reason
|
||||
}
|
||||
cl.sendNotice("Unable to join " + channel + ": You are banned" + ex)
|
||||
} else if canaccess, reason := s.canAccessChannel(cl, channel); !canaccess {
|
||||
errmsg := fmt.Sprintf("Cannot join %s: %s", channel, reason)
|
||||
cl.writeMessage(irc.ERR_INVITEONLYCHAN, []string{channel, errmsg})
|
||||
cl.sendNotice(errmsg)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -391,7 +426,6 @@ func (s *Server) revealChannelLog(channel string, client string, page int, showA
|
|||
return
|
||||
}
|
||||
|
||||
// TODO: Check channel permission
|
||||
ch := s.getChannel(channel)
|
||||
if ch == nil {
|
||||
cl.sendError("Unable to reveal, invalid channel specified")
|
||||
|
@ -574,7 +608,123 @@ func (s *Server) handleTopic(channel string, client string, topic string) {
|
|||
ch.Log(cl, irc.TOPIC, ch.topic)
|
||||
}
|
||||
|
||||
func (s *Server) handleChannelMode(c *Client, params []string) {
|
||||
ch := s.getChannel(params[0])
|
||||
if ch == nil || !s.inChannel(params[0], c.identifier) {
|
||||
return
|
||||
}
|
||||
|
||||
if len(params) == 1 || params[1] == "" {
|
||||
c.writeMessage(strings.Join([]string{irc.RPL_CHANNELMODEIS, c.nick, params[0], ch.printModes(ch.getModes(), nil)}, " "), []string{})
|
||||
|
||||
// Send channel creation time
|
||||
c.writeMessage(strings.Join([]string{"329", c.nick, params[0], fmt.Sprintf("%d", int32(ch.created))}, " "), []string{})
|
||||
} else if len(params) > 1 && (params[1] == "" || params[1][0] == '+' || params[1][0] == '-') {
|
||||
if !c.canUse(COMMAND_MODE, params[0]) {
|
||||
c.accessDenied(c.permissionRequired(COMMAND_MODE))
|
||||
return
|
||||
}
|
||||
|
||||
lastmodes := make(map[string]string)
|
||||
for m, mv := range ch.getModes() {
|
||||
lastmodes[m] = mv
|
||||
}
|
||||
|
||||
if params[1][0] == '+' {
|
||||
ch.addModes(params[1][1:])
|
||||
} else {
|
||||
ch.removeModes(params[1][1:])
|
||||
}
|
||||
s.enforceModes(params[0])
|
||||
|
||||
if reflect.DeepEqual(ch.getModes(), lastmodes) {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Check if local modes were set/unset, only send changes to local client
|
||||
addedmodes, removedmodes := ch.diffModes(lastmodes)
|
||||
|
||||
resendusercount := false
|
||||
if _, ok := addedmodes["c"]; ok {
|
||||
resendusercount = true
|
||||
}
|
||||
if _, ok := removedmodes["c"]; ok {
|
||||
resendusercount = true
|
||||
}
|
||||
if _, ok := removedmodes["D"]; ok {
|
||||
resendusercount = true
|
||||
}
|
||||
|
||||
if len(addedmodes) == 0 && len(removedmodes) == 0 {
|
||||
addedmodes = c.getModes()
|
||||
}
|
||||
|
||||
ch.clients.Range(func(k, v interface{}) bool {
|
||||
cl := s.getClient(k.(string))
|
||||
if cl != nil {
|
||||
cl.write(&prefixAnonymous, irc.MODE, []string{params[0], ch.printModes(addedmodes, removedmodes)})
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
if resendusercount {
|
||||
s.updateClientCount(params[0], c.identifier, "Enforcing MODEs")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handleUserMode(c *Client, params []string) {
|
||||
if len(params) == 1 || params[1] == "" {
|
||||
c.writeMessage(strings.Join([]string{irc.RPL_UMODEIS, c.nick, c.printModes(c.getModes(), nil)}, " "), []string{})
|
||||
return
|
||||
}
|
||||
|
||||
lastmodes := c.getModes()
|
||||
|
||||
if len(params) > 1 && len(params[1]) > 0 && (params[1][0] == '+' || params[1][0] == '-') {
|
||||
if params[1][0] == '+' {
|
||||
c.addModes(params[1][1:])
|
||||
} else {
|
||||
c.removeModes(params[1][1:])
|
||||
}
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(c.modes, lastmodes) {
|
||||
return
|
||||
}
|
||||
|
||||
addedmodes, removedmodes := c.diffModes(lastmodes)
|
||||
|
||||
resendusercount := false
|
||||
if _, ok := addedmodes["c"]; ok {
|
||||
resendusercount = true
|
||||
}
|
||||
if _, ok := removedmodes["c"]; ok {
|
||||
resendusercount = true
|
||||
}
|
||||
if _, ok := removedmodes["D"]; ok {
|
||||
resendusercount = true
|
||||
}
|
||||
|
||||
if len(addedmodes) == 0 && len(removedmodes) == 0 {
|
||||
addedmodes = c.getModes()
|
||||
}
|
||||
|
||||
c.writeMessage(strings.Join([]string{irc.MODE, c.nick}, " "), []string{c.printModes(addedmodes, removedmodes)})
|
||||
|
||||
if resendusercount {
|
||||
for ch := range s.getChannels(c.identifier) {
|
||||
s.updateClientCount(ch, c.identifier, "Enforcing MODEs")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handleMode(c *Client, params []string) {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(params) == 0 || len(params[0]) == 0 {
|
||||
c.sendNotice("Invalid use of MODE")
|
||||
return
|
||||
|
@ -585,111 +735,10 @@ func (s *Server) handleMode(c *Client, params []string) {
|
|||
return
|
||||
}
|
||||
|
||||
if validChannelPrefix(params[0]) {
|
||||
ch := s.getChannel(params[0])
|
||||
|
||||
if ch == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(params) == 1 || params[1] == "" {
|
||||
c.writeMessage(strings.Join([]string{irc.RPL_CHANNELMODEIS, c.nick, params[0], ch.printModes(ch.getModes(), nil)}, " "), []string{})
|
||||
|
||||
// Send channel creation time
|
||||
c.writeMessage(strings.Join([]string{"329", c.nick, params[0], fmt.Sprintf("%d", int32(ch.created))}, " "), []string{})
|
||||
} else if len(params) > 1 && len(params[1]) > 0 && params[1][0] == '+' || params[1][0] == '-' {
|
||||
if !c.canUse(irc.MODE, params[0]) {
|
||||
c.accessDenied(c.permissionRequired(irc.MODE))
|
||||
return
|
||||
}
|
||||
|
||||
lastmodes := make(map[string]string)
|
||||
for m, mv := range ch.getModes() {
|
||||
lastmodes[m] = mv
|
||||
}
|
||||
|
||||
if params[1][0] == '+' {
|
||||
ch.addModes(params[1][1:])
|
||||
} else {
|
||||
ch.removeModes(params[1][1:])
|
||||
}
|
||||
s.enforceModes(params[0])
|
||||
|
||||
if !reflect.DeepEqual(ch.getModes(), lastmodes) {
|
||||
// TODO: Check if local modes were set/unset, only send changes to local client
|
||||
addedmodes, removedmodes := ch.diffModes(lastmodes)
|
||||
|
||||
resendusercount := false
|
||||
if _, ok := addedmodes["c"]; ok {
|
||||
resendusercount = true
|
||||
}
|
||||
if _, ok := removedmodes["c"]; ok {
|
||||
resendusercount = true
|
||||
}
|
||||
if _, ok := removedmodes["D"]; ok {
|
||||
resendusercount = true
|
||||
}
|
||||
|
||||
if len(addedmodes) == 0 && len(removedmodes) == 0 {
|
||||
addedmodes = c.getModes()
|
||||
}
|
||||
|
||||
ch.clients.Range(func(k, v interface{}) bool {
|
||||
cl := s.getClient(k.(string))
|
||||
if cl != nil {
|
||||
cl.write(&prefixAnonymous, irc.MODE, []string{params[0], ch.printModes(addedmodes, removedmodes)})
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
if resendusercount {
|
||||
s.updateClientCount(params[0], c.identifier, "Enforcing MODEs")
|
||||
}
|
||||
}
|
||||
}
|
||||
if params[0][0] == '#' || params[0][0] == '&' {
|
||||
s.handleChannelMode(c, params)
|
||||
} else {
|
||||
if len(params) == 1 || params[1] == "" {
|
||||
c.writeMessage(strings.Join([]string{irc.RPL_UMODEIS, c.nick, c.printModes(c.getModes(), nil)}, " "), []string{})
|
||||
return
|
||||
}
|
||||
|
||||
lastmodes := c.getModes()
|
||||
|
||||
if len(params) > 1 && len(params[1]) > 0 && (params[1][0] == '+' || params[1][0] == '-') {
|
||||
if params[1][0] == '+' {
|
||||
c.addModes(params[1][1:])
|
||||
} else {
|
||||
c.removeModes(params[1][1:])
|
||||
}
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(c.modes, lastmodes) {
|
||||
addedmodes, removedmodes := c.diffModes(lastmodes)
|
||||
|
||||
resendusercount := false
|
||||
if _, ok := addedmodes["c"]; ok {
|
||||
resendusercount = true
|
||||
}
|
||||
if _, ok := removedmodes["c"]; ok {
|
||||
resendusercount = true
|
||||
}
|
||||
if _, ok := removedmodes["D"]; ok {
|
||||
resendusercount = true
|
||||
}
|
||||
|
||||
if len(addedmodes) == 0 && len(removedmodes) == 0 {
|
||||
addedmodes = c.getModes()
|
||||
}
|
||||
|
||||
c.writeMessage(strings.Join([]string{irc.MODE, c.nick}, " "), []string{c.printModes(addedmodes, removedmodes)})
|
||||
|
||||
if resendusercount {
|
||||
for ch := range s.getChannels(c.identifier) {
|
||||
s.updateClientCount(ch, c.identifier, "Enforcing MODEs")
|
||||
}
|
||||
}
|
||||
}
|
||||
s.handleUserMode(c, params)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -776,7 +825,7 @@ func (s *Server) ban(channel string, iphash string, accountid int64, expires int
|
|||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("B")
|
||||
if accountid > 0 {
|
||||
b = DBBan{Channel: generateHash(channel), Type: BAN_TYPE_ACCOUNT, Target: fmt.Sprintf("%d", accountid), Expires: expires}
|
||||
err := db.AddBan(b)
|
||||
|
@ -784,9 +833,8 @@ func (s *Server) ban(channel string, iphash string, accountid int64, expires int
|
|||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("C")
|
||||
if b.Channel == "" {
|
||||
log.Println("blank chan")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -796,7 +844,7 @@ func (s *Server) ban(channel string, iphash string, accountid int64, expires int
|
|||
ch = ""
|
||||
rs = formatAction("Killed", reason)
|
||||
}
|
||||
|
||||
log.Println("D")
|
||||
cls := s.getClients(ch)
|
||||
for _, cl := range cls {
|
||||
if cl == nil {
|
||||
|
@ -841,8 +889,27 @@ func (s *Server) handleUserCommand(client string, command string, params []strin
|
|||
s.sendUsage(cl, cmd)
|
||||
return
|
||||
case COMMAND_INFO:
|
||||
// TODO: when channel is supplied, send whether it is registered and show a notice that it is dropping soon if no super admins have logged in in X days
|
||||
cl.sendMessage("Server info: AnonIRCd https://github.com/sageru-6ch/anonircd")
|
||||
if len(params) > 0 && len(params[0]) > 0 {
|
||||
if canaccess, reason := s.canAccessChannel(cl, params[0]); !canaccess {
|
||||
cl.sendError("Failed to fetch channel INFO, " + reason)
|
||||
return
|
||||
}
|
||||
|
||||
dbch, err := db.Channel(params[0])
|
||||
if err != nil {
|
||||
cl.sendError("Failed to fetch channel INFO, " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
chst := "Unfounded"
|
||||
if dbch.Channel != "" {
|
||||
chst = "Founded"
|
||||
}
|
||||
|
||||
cl.sendMessage(fmt.Sprintf("%s: %s", params[0], chst))
|
||||
} else {
|
||||
cl.sendMessage("AnonIRCd https://github.com/sageru-6ch/anonircd")
|
||||
}
|
||||
return
|
||||
case COMMAND_REGISTER:
|
||||
if len(params) == 0 {
|
||||
|
@ -945,7 +1012,6 @@ func (s *Server) handleUserCommand(client string, command string, params []strin
|
|||
}
|
||||
cl.sendMessage("Password changed successfully")
|
||||
case COMMAND_REVEAL, COMMAND_AUDIT:
|
||||
// TODO: &#chan shows moderator audit log, & alone shows server admin audit log
|
||||
if len(params) == 0 {
|
||||
s.sendUsage(cl, command)
|
||||
return
|
||||
|
@ -1044,6 +1110,7 @@ func (s *Server) handleUserCommand(client string, command string, params []strin
|
|||
bch = CHANNEL_SERVER
|
||||
}
|
||||
err := s.ban(bch, rcl.iphash, rcl.account, expires, reason)
|
||||
log.Println("A")
|
||||
if err != nil {
|
||||
cl.sendError(fmt.Sprintf("Unable to %s, %v", strings.ToLower(command), err))
|
||||
return
|
||||
|
@ -1051,7 +1118,6 @@ func (s *Server) handleUserCommand(client string, command string, params []strin
|
|||
|
||||
cl.sendMessage(fmt.Sprintf("%sed %s %s", strings.ToLower(command), params[0], params[1]))
|
||||
case COMMAND_STATS:
|
||||
|
||||
cl.sendMessage(fmt.Sprintf("%d clients in %d channels", s.clientCount(), s.channelCount()))
|
||||
case COMMAND_REHASH:
|
||||
|
||||
|
@ -1066,41 +1132,46 @@ func (s *Server) handleUserCommand(client string, command string, params []strin
|
|||
}
|
||||
}
|
||||
|
||||
func (s *Server) handlePrivmsg(channel string, client string, message string) {
|
||||
func (s *Server) handlePrivmsg(target string, client string, message string) {
|
||||
cl := s.getClient(client)
|
||||
if cl == nil {
|
||||
if cl == nil || len(target) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if strings.ToLower(channel) == "anonirc" {
|
||||
if strings.ToLower(target) == "anonirc" {
|
||||
params := strings.Split(message, " ")
|
||||
if len(params) > 0 && len(params[0]) > 0 {
|
||||
var otherparams []string
|
||||
if len(params) > 1 {
|
||||
otherparams = params[1:]
|
||||
}
|
||||
|
||||
s.handleUserCommand(client, params[0], otherparams)
|
||||
if len(params) == 0 || len(params[0]) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var otherparams []string
|
||||
if len(params) > 1 {
|
||||
otherparams = params[1:]
|
||||
}
|
||||
|
||||
s.handleUserCommand(client, params[0], otherparams)
|
||||
return
|
||||
} else if channel == "" || !validChannelPrefix(channel) {
|
||||
} else if target[0] != '#' && target[0] != '&' {
|
||||
cl.writeMessage(irc.ERR_NOSUCHNICK, []string{target, "No such nick/channel"})
|
||||
return
|
||||
} else if !s.inChannel(target, client) {
|
||||
cl.writeMessage(irc.ERR_CANNOTSENDTOCHAN, []string{target, fmt.Sprintf("No external channel messages (%s)", target)})
|
||||
return
|
||||
} else if !s.inChannel(channel, client) {
|
||||
return // Not in channel
|
||||
}
|
||||
|
||||
ch := s.getChannel(channel)
|
||||
ch := s.getChannel(target)
|
||||
if ch == nil {
|
||||
return
|
||||
} else if ch.hasMode("m") && cl.getPermission(target) < PERMISSION_VIP {
|
||||
cl.writeMessage(irc.ERR_CANNOTSENDTOCHAN, []string{target, fmt.Sprintf("Channel is moderated, only VIP may speak (%s)", target)})
|
||||
return
|
||||
}
|
||||
|
||||
s.updateClientCount(channel, "", "")
|
||||
|
||||
s.updateClientCount(target, "", "")
|
||||
ch.clients.Range(func(k, v interface{}) bool {
|
||||
chcl := s.getClient(k.(string))
|
||||
if chcl != nil && chcl.identifier != client {
|
||||
chcl.write(&prefixAnonymous, irc.PRIVMSG, []string{channel, message})
|
||||
chcl.write(&prefixAnonymous, irc.PRIVMSG, []string{target, message})
|
||||
}
|
||||
|
||||
return true
|
||||
|
@ -1114,19 +1185,19 @@ func (s *Server) handleRead(c *Client) {
|
|||
return
|
||||
}
|
||||
|
||||
c.conn.SetReadDeadline(time.Now().Add(300 * time.Second))
|
||||
|
||||
if _, ok := s.clients.Load(c.identifier); !ok {
|
||||
s.killClient(c, "")
|
||||
return
|
||||
}
|
||||
|
||||
c.conn.SetReadDeadline(time.Now().Add(300 * time.Second))
|
||||
msg, err := c.reader.Decode()
|
||||
if msg == nil || err != nil {
|
||||
// Error decoding message, client probably disconnected
|
||||
s.killClient(c, "")
|
||||
return
|
||||
}
|
||||
|
||||
if debugMode && (verbose || len(msg.Command) < 4 || (msg.Command[0:4] != irc.PING && msg.Command[0:4] != irc.PONG)) {
|
||||
log.Printf("%s -> %s", c.identifier, msg)
|
||||
}
|
||||
|
@ -1142,12 +1213,11 @@ func (s *Server) handleRead(c *Client) {
|
|||
c.writeMessage(irc.RPL_CREATED, []string{fmt.Sprintf("This server was created %s", time.Unix(s.created, 0).UTC())})
|
||||
c.writeMessage(strings.Join([]string{irc.RPL_MYINFO, c.nick, "AnonIRC", "AnonIRCd", CLIENT_MODES, CHANNEL_MODES, CHANNEL_MODES_ARG}, " "), []string{})
|
||||
|
||||
motdsplit := strings.Split(motd, "\n")
|
||||
for i, motdmsg := range motdsplit {
|
||||
for i, motdmsg := range s.motd {
|
||||
var motdcode string
|
||||
if i == 0 {
|
||||
motdcode = irc.RPL_MOTDSTART
|
||||
} else if i < len(motdsplit)-1 {
|
||||
} else if i < len(s.motd)-1 {
|
||||
motdcode = irc.RPL_MOTD
|
||||
} else {
|
||||
motdcode = irc.RPL_ENDOFMOTD
|
||||
|
@ -1335,23 +1405,27 @@ func (s *Server) handleConnection(conn net.Conn, ssl bool) {
|
|||
}
|
||||
}
|
||||
|
||||
client := NewClient(identifier, conn, ssl)
|
||||
c := NewClient(identifier, conn, ssl)
|
||||
banned := true
|
||||
reason := ""
|
||||
if client != nil {
|
||||
banned, reason = client.isBanned("")
|
||||
if c != nil {
|
||||
banned, reason = c.isBanned(CHANNEL_SERVER)
|
||||
}
|
||||
if banned {
|
||||
// TODO: Send banned message
|
||||
_ = reason
|
||||
return // Banned
|
||||
|
||||
go s.handleWrite(c)
|
||||
if !banned {
|
||||
s.clients.Store(c.identifier, c)
|
||||
s.handleRead(c) // Block until the connection is closed
|
||||
} else {
|
||||
if reason != "" {
|
||||
reason = fmt.Sprintf(" (%s)", reason)
|
||||
}
|
||||
c.writeMessage(irc.ERR_YOUREBANNEDCREEP, []string{"You are banned from this server" + reason})
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
s.clients.Store(client.identifier, client)
|
||||
|
||||
go s.handleWrite(client)
|
||||
s.handleRead(client) // Block until the connection is closed
|
||||
|
||||
s.killClient(client, "")
|
||||
s.killClient(c, "")
|
||||
s.clients.Delete(identifier)
|
||||
}
|
||||
|
||||
func (s *Server) killClient(c *Client, reason string) {
|
||||
|
@ -1493,6 +1567,12 @@ func (s *Server) loadConfig() error {
|
|||
return errors.New(fmt.Sprintf("DBDriver and DBSource must be configured in %s\nExample:\n\nDBDriver=\"sqlite3\"\nDBSource=\"/home/user/anonircd/anonircd.db\"", s.configfile))
|
||||
}
|
||||
|
||||
motd := DEFAULT_MOTD
|
||||
if s.config.MOTD != "" {
|
||||
motd = s.config.MOTD
|
||||
}
|
||||
s.motd = strings.Split(strings.TrimRight(motd, " \t\r\n"), "\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -55,10 +55,6 @@ func randomIdentifier() string {
|
|||
return string(b)
|
||||
}
|
||||
|
||||
func validChannelPrefix(channel string) bool {
|
||||
return channel[0] == '&' || channel[0] == '#'
|
||||
}
|
||||
|
||||
func generateHash(s string) string {
|
||||
sha512 := sha3.New512()
|
||||
_, err := sha512.Write([]byte(strings.Join([]string{s, fmt.Sprintf("%x", md5.Sum([]byte(s))), s}, "-")))
|
||||
|
|
|
@ -163,16 +163,18 @@ func bindArgs(names []string, arg interface{}, m *reflectx.Mapper) ([]interface{
|
|||
v = v.Elem()
|
||||
}
|
||||
|
||||
fields := m.TraversalsByName(v.Type(), names)
|
||||
for i, t := range fields {
|
||||
err := m.TraversalsByNameFunc(v.Type(), names, func(i int, t []int) error {
|
||||
if len(t) == 0 {
|
||||
return arglist, fmt.Errorf("could not find name %s in %#v", names[i], arg)
|
||||
return fmt.Errorf("could not find name %s in %#v", names[i], arg)
|
||||
}
|
||||
|
||||
val := reflectx.FieldByIndexesReadOnly(v, t)
|
||||
arglist = append(arglist, val.Interface())
|
||||
}
|
||||
|
||||
return arglist, nil
|
||||
return nil
|
||||
})
|
||||
|
||||
return arglist, err
|
||||
}
|
||||
|
||||
// like bindArgs, but for maps.
|
||||
|
|
|
@ -166,20 +166,39 @@ func (m *Mapper) FieldsByName(v reflect.Value, names []string) []reflect.Value {
|
|||
// traversals for each mapped name. Panics if t is not a struct or Indirectable
|
||||
// to a struct. Returns empty int slice for each name not found.
|
||||
func (m *Mapper) TraversalsByName(t reflect.Type, names []string) [][]int {
|
||||
r := make([][]int, 0, len(names))
|
||||
m.TraversalsByNameFunc(t, names, func(_ int, i []int) error {
|
||||
if i == nil {
|
||||
r = append(r, []int{})
|
||||
} else {
|
||||
r = append(r, i)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return r
|
||||
}
|
||||
|
||||
// TraversalsByNameFunc traverses the mapped names and calls fn with the index of
|
||||
// each name and the struct traversal represented by that name. Panics if t is not
|
||||
// a struct or Indirectable to a struct. Returns the first error returned by fn or nil.
|
||||
func (m *Mapper) TraversalsByNameFunc(t reflect.Type, names []string, fn func(int, []int) error) error {
|
||||
t = Deref(t)
|
||||
mustBe(t, reflect.Struct)
|
||||
tm := m.TypeMap(t)
|
||||
|
||||
r := make([][]int, 0, len(names))
|
||||
for _, name := range names {
|
||||
for i, name := range names {
|
||||
fi, ok := tm.Names[name]
|
||||
if !ok {
|
||||
r = append(r, []int{})
|
||||
if err := fn(i, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
r = append(r, fi.Index)
|
||||
if err := fn(i, fi.Index); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return r
|
||||
return nil
|
||||
}
|
||||
|
||||
// FieldByIndexes returns a value for the field given by the struct traversal
|
||||
|
|
|
@ -903,3 +903,72 @@ func BenchmarkFieldByIndexL4(b *testing.B) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTraversalsByName(b *testing.B) {
|
||||
type A struct {
|
||||
Value int
|
||||
}
|
||||
|
||||
type B struct {
|
||||
A A
|
||||
}
|
||||
|
||||
type C struct {
|
||||
B B
|
||||
}
|
||||
|
||||
type D struct {
|
||||
C C
|
||||
}
|
||||
|
||||
m := NewMapper("")
|
||||
t := reflect.TypeOf(D{})
|
||||
names := []string{"C", "B", "A", "Value"}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
if l := len(m.TraversalsByName(t, names)); l != len(names) {
|
||||
b.Errorf("expected %d values, got %d", len(names), l)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTraversalsByNameFunc(b *testing.B) {
|
||||
type A struct {
|
||||
Z int
|
||||
}
|
||||
|
||||
type B struct {
|
||||
A A
|
||||
}
|
||||
|
||||
type C struct {
|
||||
B B
|
||||
}
|
||||
|
||||
type D struct {
|
||||
C C
|
||||
}
|
||||
|
||||
m := NewMapper("")
|
||||
t := reflect.TypeOf(D{})
|
||||
names := []string{"C", "B", "A", "Z", "Y"}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
var l int
|
||||
|
||||
if err := m.TraversalsByNameFunc(t, names, func(_ int, _ []int) error {
|
||||
l++
|
||||
return nil
|
||||
}); err != nil {
|
||||
b.Errorf("unexpected error %s", err)
|
||||
}
|
||||
|
||||
if l != len(names) {
|
||||
b.Errorf("expected %d values, got %d", len(names), l)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,219 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package argon2 implements the key derivation function Argon2.
|
||||
// Argon2 was selected as the winner of the Password Hashing Competition and can
|
||||
// be used to derive cryptographic keys from passwords.
|
||||
// Argon2 is specfifed at https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
|
||||
package argon2
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/crypto/blake2b"
|
||||
)
|
||||
|
||||
// The Argon2 version implemented by this package.
|
||||
const Version = 0x13
|
||||
|
||||
const (
|
||||
argon2d = iota
|
||||
argon2i
|
||||
argon2id
|
||||
)
|
||||
|
||||
// Key derives a key from the password, salt, and cost parameters using Argon2i
|
||||
// returning a byte slice of length keyLen that can be used as cryptographic key.
|
||||
// The CPU cost and parallism degree must be greater than zero.
|
||||
//
|
||||
// For example, you can get a derived key for e.g. AES-256 (which needs a 32-byte key) by doing:
|
||||
// `key := argon2.Key([]byte("some password"), salt, 4, 32*1024, 4, 32)`
|
||||
//
|
||||
// The recommended parameters for interactive logins as of 2017 are time=4, memory=32*1024.
|
||||
// The number of threads can be adjusted to the numbers of available CPUs.
|
||||
// The time parameter specifies the number of passes over the memory and the memory
|
||||
// parameter specifies the size of the memory in KiB. For example memory=32*1024 sets the
|
||||
// memory cost to ~32 MB.
|
||||
// The cost parameters should be increased as memory latency and CPU parallelism increases.
|
||||
// Remember to get a good random salt.
|
||||
func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
|
||||
return deriveKey(argon2i, password, salt, nil, nil, time, memory, threads, keyLen)
|
||||
}
|
||||
|
||||
func deriveKey(mode int, password, salt, secret, data []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
|
||||
if time < 1 {
|
||||
panic("argon2: number of rounds too small")
|
||||
}
|
||||
if threads < 1 {
|
||||
panic("argon2: paralisim degree too low")
|
||||
}
|
||||
mem := memory / (4 * uint32(threads)) * (4 * uint32(threads))
|
||||
if mem < 8*uint32(threads) {
|
||||
mem = 8 * uint32(threads)
|
||||
}
|
||||
B := initBlocks(password, salt, secret, data, time, mem, uint32(threads), keyLen, mode)
|
||||
processBlocks(B, time, mem, uint32(threads), mode)
|
||||
return extractKey(B, mem, uint32(threads), keyLen)
|
||||
}
|
||||
|
||||
const blockLength = 128
|
||||
|
||||
type block [blockLength]uint64
|
||||
|
||||
func initBlocks(password, salt, key, data []byte, time, memory, threads, keyLen uint32, mode int) []block {
|
||||
var (
|
||||
block0 [1024]byte
|
||||
h0 [blake2b.Size + 8]byte
|
||||
params [24]byte
|
||||
tmp [4]byte
|
||||
)
|
||||
|
||||
b2, _ := blake2b.New512(nil)
|
||||
binary.LittleEndian.PutUint32(params[0:4], threads)
|
||||
binary.LittleEndian.PutUint32(params[4:8], keyLen)
|
||||
binary.LittleEndian.PutUint32(params[8:12], memory)
|
||||
binary.LittleEndian.PutUint32(params[12:16], time)
|
||||
binary.LittleEndian.PutUint32(params[16:20], uint32(Version))
|
||||
binary.LittleEndian.PutUint32(params[20:24], uint32(mode))
|
||||
b2.Write(params[:])
|
||||
binary.LittleEndian.PutUint32(tmp[:], uint32(len(password)))
|
||||
b2.Write(tmp[:])
|
||||
b2.Write(password)
|
||||
binary.LittleEndian.PutUint32(tmp[:], uint32(len(salt)))
|
||||
b2.Write(tmp[:])
|
||||
b2.Write(salt)
|
||||
binary.LittleEndian.PutUint32(tmp[:], uint32(len(key)))
|
||||
b2.Write(tmp[:])
|
||||
b2.Write(key)
|
||||
binary.LittleEndian.PutUint32(tmp[:], uint32(len(data)))
|
||||
b2.Write(tmp[:])
|
||||
b2.Write(data)
|
||||
b2.Sum(h0[:0])
|
||||
|
||||
B := make([]block, memory)
|
||||
for lane := uint32(0); lane < threads; lane++ {
|
||||
j := lane * (memory / threads)
|
||||
binary.LittleEndian.PutUint32(h0[blake2b.Size+4:], lane)
|
||||
|
||||
binary.LittleEndian.PutUint32(h0[blake2b.Size:], 0)
|
||||
blake2bHash(block0[:], h0[:])
|
||||
for i := range B[0] {
|
||||
B[j+0][i] = binary.LittleEndian.Uint64(block0[i*8:])
|
||||
}
|
||||
|
||||
binary.LittleEndian.PutUint32(h0[blake2b.Size:], 1)
|
||||
blake2bHash(block0[:], h0[:])
|
||||
for i := range B[0] {
|
||||
B[j+1][i] = binary.LittleEndian.Uint64(block0[i*8:])
|
||||
}
|
||||
}
|
||||
return B
|
||||
}
|
||||
|
||||
func processBlocks(B []block, time, memory, threads uint32, mode int) {
|
||||
const syncPoints = 4
|
||||
lanes := memory / threads
|
||||
segments := lanes / syncPoints
|
||||
|
||||
processSegment := func(n, slice, lane uint32, wg *sync.WaitGroup) {
|
||||
var addresses, in, zero block
|
||||
if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {
|
||||
in[0] = uint64(n)
|
||||
in[1] = uint64(lane)
|
||||
in[2] = uint64(slice)
|
||||
in[3] = uint64(memory)
|
||||
in[4] = uint64(time)
|
||||
in[5] = uint64(mode)
|
||||
}
|
||||
|
||||
index := uint32(0)
|
||||
if n == 0 && slice == 0 {
|
||||
index = 2 // we have already generated the first two blocks
|
||||
if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {
|
||||
in[6]++
|
||||
processBlock(&addresses, &in, &zero)
|
||||
processBlock(&addresses, &addresses, &zero)
|
||||
}
|
||||
}
|
||||
|
||||
offset := lane*lanes + slice*segments + index
|
||||
var random uint64
|
||||
for index < segments {
|
||||
prev := offset - 1
|
||||
if index == 0 && slice == 0 {
|
||||
prev = lane*lanes + lanes - 1 // last block in lane
|
||||
}
|
||||
if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {
|
||||
if index%blockLength == 0 {
|
||||
in[6]++
|
||||
processBlock(&addresses, &in, &zero)
|
||||
processBlock(&addresses, &addresses, &zero)
|
||||
}
|
||||
random = addresses[index%blockLength]
|
||||
} else {
|
||||
random = B[prev][0]
|
||||
}
|
||||
newOffset := indexAlpha(random, lanes, segments, threads, n, slice, lane, index)
|
||||
processBlockXOR(&B[offset], &B[prev], &B[newOffset])
|
||||
index, offset = index+1, offset+1
|
||||
}
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
for n := uint32(0); n < time; n++ {
|
||||
for slice := uint32(0); slice < syncPoints; slice++ {
|
||||
var wg sync.WaitGroup
|
||||
for lane := uint32(0); lane < threads; lane++ {
|
||||
wg.Add(1)
|
||||
go processSegment(n, slice, lane, &wg)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func extractKey(B []block, memory, threads, keyLen uint32) []byte {
|
||||
lanes := memory / threads
|
||||
for lane := uint32(0); lane < threads-1; lane++ {
|
||||
for i, v := range B[(lane*lanes)+lanes-1] {
|
||||
B[memory-1][i] ^= v
|
||||
}
|
||||
}
|
||||
|
||||
var block [1024]byte
|
||||
for i, v := range B[memory-1] {
|
||||
binary.LittleEndian.PutUint64(block[i*8:], v)
|
||||
}
|
||||
key := make([]byte, keyLen)
|
||||
blake2bHash(key, block[:])
|
||||
return key
|
||||
}
|
||||
|
||||
func indexAlpha(rand uint64, lanes, segments, threads, n, slice, lane, index uint32) uint32 {
|
||||
refLane := uint32(rand>>32) % threads
|
||||
|
||||
m, s := 3*segments, (slice+1)%4*segments
|
||||
if lane == refLane {
|
||||
m += index
|
||||
}
|
||||
if n == 0 {
|
||||
m, s = slice*segments, 0
|
||||
if slice == 0 || lane == refLane {
|
||||
m += index
|
||||
}
|
||||
}
|
||||
if index == 0 || lane == refLane {
|
||||
m--
|
||||
}
|
||||
return phi(rand, uint64(m), uint64(s), refLane, lanes)
|
||||
}
|
||||
|
||||
func phi(rand, m, s uint64, lane, lanes uint32) uint32 {
|
||||
p := rand & 0xFFFFFFFF
|
||||
p = (p * p) >> 32
|
||||
p = (p * m) >> 32
|
||||
return lane*lanes + uint32((s+m-(p+1))%uint64(lanes))
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
package argon2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
genKatPassword = []byte{
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
}
|
||||
genKatSalt = []byte{0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02}
|
||||
genKatSecret = []byte{0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03}
|
||||
genKatAAD = []byte{0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}
|
||||
)
|
||||
|
||||
func TestArgon2(t *testing.T) {
|
||||
defer func(sse4 bool) { useSSE4 = sse4 }(useSSE4)
|
||||
|
||||
if useSSE4 {
|
||||
t.Log("SSE4.1 version")
|
||||
testArgon2i(t)
|
||||
testArgon2d(t)
|
||||
testArgon2id(t)
|
||||
useSSE4 = false
|
||||
}
|
||||
t.Log("generic version")
|
||||
testArgon2i(t)
|
||||
testArgon2d(t)
|
||||
testArgon2id(t)
|
||||
}
|
||||
|
||||
func testArgon2d(t *testing.T) {
|
||||
want := []byte{
|
||||
0x51, 0x2b, 0x39, 0x1b, 0x6f, 0x11, 0x62, 0x97,
|
||||
0x53, 0x71, 0xd3, 0x09, 0x19, 0x73, 0x42, 0x94,
|
||||
0xf8, 0x68, 0xe3, 0xbe, 0x39, 0x84, 0xf3, 0xc1,
|
||||
0xa1, 0x3a, 0x4d, 0xb9, 0xfa, 0xbe, 0x4a, 0xcb,
|
||||
}
|
||||
hash := deriveKey(argon2d, genKatPassword, genKatSalt, genKatSecret, genKatAAD, 3, 32, 4, 32)
|
||||
if !bytes.Equal(hash, want) {
|
||||
t.Errorf("derived key does not match - got: %s , want: %s", hex.EncodeToString(hash), hex.EncodeToString(want))
|
||||
}
|
||||
}
|
||||
|
||||
func testArgon2i(t *testing.T) {
|
||||
want := []byte{
|
||||
0xc8, 0x14, 0xd9, 0xd1, 0xdc, 0x7f, 0x37, 0xaa,
|
||||
0x13, 0xf0, 0xd7, 0x7f, 0x24, 0x94, 0xbd, 0xa1,
|
||||
0xc8, 0xde, 0x6b, 0x01, 0x6d, 0xd3, 0x88, 0xd2,
|
||||
0x99, 0x52, 0xa4, 0xc4, 0x67, 0x2b, 0x6c, 0xe8,
|
||||
}
|
||||
hash := deriveKey(argon2i, genKatPassword, genKatSalt, genKatSecret, genKatAAD, 3, 32, 4, 32)
|
||||
if !bytes.Equal(hash, want) {
|
||||
t.Errorf("derived key does not match - got: %s , want: %s", hex.EncodeToString(hash), hex.EncodeToString(want))
|
||||
}
|
||||
}
|
||||
|
||||
func testArgon2id(t *testing.T) {
|
||||
want := []byte{
|
||||
0x0d, 0x64, 0x0d, 0xf5, 0x8d, 0x78, 0x76, 0x6c,
|
||||
0x08, 0xc0, 0x37, 0xa3, 0x4a, 0x8b, 0x53, 0xc9,
|
||||
0xd0, 0x1e, 0xf0, 0x45, 0x2d, 0x75, 0xb6, 0x5e,
|
||||
0xb5, 0x25, 0x20, 0xe9, 0x6b, 0x01, 0xe6, 0x59,
|
||||
}
|
||||
hash := deriveKey(argon2id, genKatPassword, genKatSalt, genKatSecret, genKatAAD, 3, 32, 4, 32)
|
||||
if !bytes.Equal(hash, want) {
|
||||
t.Errorf("derived key does not match - got: %s , want: %s", hex.EncodeToString(hash), hex.EncodeToString(want))
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkArgon2(mode int, time, memory uint32, threads uint8, keyLen uint32, b *testing.B) {
|
||||
password := []byte("password")
|
||||
salt := []byte("choosing random salts is hard")
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
deriveKey(mode, password, salt, nil, nil, time, memory, threads, keyLen)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkArgon2i(b *testing.B) {
|
||||
b.Run(" Time: 3 Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2i, 3, 32*1024, 1, 32, b) })
|
||||
b.Run(" Time: 4 Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2i, 4, 32*1024, 1, 32, b) })
|
||||
b.Run(" Time: 5 Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2i, 5, 32*1024, 1, 32, b) })
|
||||
b.Run(" Time: 3 Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2i, 3, 64*1024, 4, 32, b) })
|
||||
b.Run(" Time: 4 Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2i, 4, 64*1024, 4, 32, b) })
|
||||
b.Run(" Time: 5 Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2i, 5, 64*1024, 4, 32, b) })
|
||||
}
|
||||
|
||||
func BenchmarkArgon2d(b *testing.B) {
|
||||
b.Run(" Time: 3, Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2d, 3, 32*1024, 1, 32, b) })
|
||||
b.Run(" Time: 4, Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2d, 4, 32*1024, 1, 32, b) })
|
||||
b.Run(" Time: 5, Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2d, 5, 32*1024, 1, 32, b) })
|
||||
b.Run(" Time: 3, Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2d, 3, 64*1024, 4, 32, b) })
|
||||
b.Run(" Time: 4, Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2d, 4, 64*1024, 4, 32, b) })
|
||||
b.Run(" Time: 5, Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2d, 5, 64*1024, 4, 32, b) })
|
||||
}
|
||||
|
||||
func BenchmarkArgon2id(b *testing.B) {
|
||||
b.Run(" Time: 3, Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2id, 3, 32*1024, 1, 32, b) })
|
||||
b.Run(" Time: 4, Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2id, 4, 32*1024, 1, 32, b) })
|
||||
b.Run(" Time: 5, Memory: 32 MB, Threads: 1", func(b *testing.B) { benchmarkArgon2(argon2id, 5, 32*1024, 1, 32, b) })
|
||||
b.Run(" Time: 3, Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2id, 3, 64*1024, 4, 32, b) })
|
||||
b.Run(" Time: 4, Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2id, 4, 64*1024, 4, 32, b) })
|
||||
b.Run(" Time: 5, Memory: 64 MB, Threads: 4", func(b *testing.B) { benchmarkArgon2(argon2id, 5, 64*1024, 4, 32, b) })
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package argon2
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"hash"
|
||||
|
||||
"golang.org/x/crypto/blake2b"
|
||||
)
|
||||
|
||||
// blake2bHash computes an arbitrary long hash value of in
|
||||
// and writes the hash to out.
|
||||
func blake2bHash(out []byte, in []byte) {
|
||||
var b2 hash.Hash
|
||||
if n := len(out); n < blake2b.Size {
|
||||
b2, _ = blake2b.New(n, nil)
|
||||
} else {
|
||||
b2, _ = blake2b.New512(nil)
|
||||
}
|
||||
|
||||
var buffer [blake2b.Size]byte
|
||||
binary.LittleEndian.PutUint32(buffer[:4], uint32(len(out)))
|
||||
b2.Write(buffer[:4])
|
||||
b2.Write(in)
|
||||
|
||||
if len(out) <= blake2b.Size {
|
||||
b2.Sum(out[:0])
|
||||
return
|
||||
}
|
||||
|
||||
outLen := len(out)
|
||||
b2.Sum(buffer[:0])
|
||||
b2.Reset()
|
||||
copy(out, buffer[:32])
|
||||
out = out[32:]
|
||||
for len(out) > blake2b.Size {
|
||||
b2.Write(buffer[:])
|
||||
b2.Sum(buffer[:0])
|
||||
copy(out, buffer[:32])
|
||||
out = out[32:]
|
||||
b2.Reset()
|
||||
}
|
||||
|
||||
if outLen%blake2b.Size > 0 { // outLen > 64
|
||||
r := ((outLen + 31) / 32) - 2 // ⌈τ /32⌉-2
|
||||
b2, _ = blake2b.New(outLen-32*r, nil)
|
||||
}
|
||||
b2.Write(buffer[:])
|
||||
b2.Sum(out[:0])
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package argon2
|
||||
|
||||
func init() {
|
||||
useSSE4 = supportsSSE4()
|
||||
}
|
||||
|
||||
//go:noescape
|
||||
func supportsSSE4() bool
|
||||
|
||||
//go:noescape
|
||||
func mixBlocksSSE2(out, a, b, c *block)
|
||||
|
||||
//go:noescape
|
||||
func xorBlocksSSE2(out, a, b, c *block)
|
||||
|
||||
//go:noescape
|
||||
func blamkaSSE4(b *block)
|
||||
|
||||
func processBlockSSE(out, in1, in2 *block, xor bool) {
|
||||
var t block
|
||||
mixBlocksSSE2(&t, in1, in2, &t)
|
||||
if useSSE4 {
|
||||
blamkaSSE4(&t)
|
||||
} else {
|
||||
for i := 0; i < blockLength; i += 16 {
|
||||
blamkaGeneric(
|
||||
&t[i+0], &t[i+1], &t[i+2], &t[i+3],
|
||||
&t[i+4], &t[i+5], &t[i+6], &t[i+7],
|
||||
&t[i+8], &t[i+9], &t[i+10], &t[i+11],
|
||||
&t[i+12], &t[i+13], &t[i+14], &t[i+15],
|
||||
)
|
||||
}
|
||||
for i := 0; i < blockLength/8; i += 2 {
|
||||
blamkaGeneric(
|
||||
&t[i], &t[i+1], &t[16+i], &t[16+i+1],
|
||||
&t[32+i], &t[32+i+1], &t[48+i], &t[48+i+1],
|
||||
&t[64+i], &t[64+i+1], &t[80+i], &t[80+i+1],
|
||||
&t[96+i], &t[96+i+1], &t[112+i], &t[112+i+1],
|
||||
)
|
||||
}
|
||||
}
|
||||
if xor {
|
||||
xorBlocksSSE2(out, in1, in2, &t)
|
||||
} else {
|
||||
mixBlocksSSE2(out, in1, in2, &t)
|
||||
}
|
||||
}
|
||||
|
||||
func processBlock(out, in1, in2 *block) {
|
||||
processBlockSSE(out, in1, in2, false)
|
||||
}
|
||||
|
||||
func processBlockXOR(out, in1, in2 *block) {
|
||||
processBlockSSE(out, in1, in2, true)
|
||||
}
|
|
@ -0,0 +1,252 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build amd64,!gccgo,!appengine
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
DATA ·c40<>+0x00(SB)/8, $0x0201000706050403
|
||||
DATA ·c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b
|
||||
GLOBL ·c40<>(SB), (NOPTR+RODATA), $16
|
||||
|
||||
DATA ·c48<>+0x00(SB)/8, $0x0100070605040302
|
||||
DATA ·c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a
|
||||
GLOBL ·c48<>(SB), (NOPTR+RODATA), $16
|
||||
|
||||
#define SHUFFLE(v2, v3, v4, v5, v6, v7, t1, t2) \
|
||||
MOVO v4, t1; \
|
||||
MOVO v5, v4; \
|
||||
MOVO t1, v5; \
|
||||
MOVO v6, t1; \
|
||||
PUNPCKLQDQ v6, t2; \
|
||||
PUNPCKHQDQ v7, v6; \
|
||||
PUNPCKHQDQ t2, v6; \
|
||||
PUNPCKLQDQ v7, t2; \
|
||||
MOVO t1, v7; \
|
||||
MOVO v2, t1; \
|
||||
PUNPCKHQDQ t2, v7; \
|
||||
PUNPCKLQDQ v3, t2; \
|
||||
PUNPCKHQDQ t2, v2; \
|
||||
PUNPCKLQDQ t1, t2; \
|
||||
PUNPCKHQDQ t2, v3
|
||||
|
||||
#define SHUFFLE_INV(v2, v3, v4, v5, |