Add channel modes +i, +m and +r, MOTD config option and banned message when connecting
This commit is contained in:
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, v6, v7, t1, t2) \
|
||||
MOVO v4, t1; \
|
||||
MOVO v5, v4; \
|
||||
MOVO t1, v5; \
|
||||
MOVO v2, t1; \
|
||||
PUNPCKLQDQ v2, t2; \
|
||||
PUNPCKHQDQ v3, v2; \
|
||||
PUNPCKHQDQ t2, v2; \
|
||||
PUNPCKLQDQ v3, t2; \
|
||||
MOVO t1, v3; \
|
||||
MOVO v6, t1; \
|
||||
PUNPCKHQDQ t2, v3; \
|
||||
PUNPCKLQDQ v7, t2; \
|
||||
PUNPCKHQDQ t2, v6; \
|
||||
PUNPCKLQDQ t1, t2; \
|
||||
PUNPCKHQDQ t2, v7
|
||||
|
||||
#define HALF_ROUND(v0, v1, v2, v3, v4, v5, v6, v7, t0, c40, c48) \
|
||||
MOVO v0, t0; \
|
||||
PMULULQ v2, t0; \
|
||||
PADDQ v2, v0; \
|
||||
PADDQ t0, v0; \
|
||||
PADDQ t0, v0; \
|
||||
PXOR v0, v6; \
|
||||
PSHUFD $0xB1, v6, v6; \
|
||||
MOVO v4, t0; \
|
||||
PMULULQ v6, t0; \
|
||||
PADDQ v6, v4; \
|
||||
PADDQ t0, v4; \
|
||||
PADDQ t0, v4; \
|
||||
PXOR v4, v2; \
|
||||
PSHUFB c40, v2; \
|
||||
MOVO v0, t0; \
|
||||
PMULULQ v2, t0; \
|
||||
PADDQ v2, v0; \
|
||||
PADDQ t0, v0; \
|
||||
PADDQ t0, v0; \
|
||||
PXOR v0, v6; \
|
||||
PSHUFB c48, v6; \
|
||||
MOVO v4, t0; \
|
||||
PMULULQ v6, t0; \
|
||||
PADDQ v6, v4; \
|
||||
PADDQ t0, v4; \
|
||||
PADDQ t0, v4; \
|
||||
PXOR v4, v2; \
|
||||
MOVO v2, t0; \
|
||||
PADDQ v2, t0; \
|
||||
PSRLQ $63, v2; \
|
||||
PXOR t0, v2; \
|
||||
MOVO v1, t0; \
|
||||
PMULULQ v3, t0; \
|
||||
PADDQ v3, v1; \
|
||||
PADDQ t0, v1; \
|
||||
PADDQ t0, v1; \
|
||||
PXOR v1, v7; \
|
||||
PSHUFD $0xB1, v7, v7; \
|
||||
MOVO v5, t0; \
|
||||
PMULULQ v7, t0; \
|
||||
PADDQ v7, v5; \
|
||||
PADDQ t0, v5; \
|
||||
PADDQ t0, v5; \
|
||||
PXOR v5, v3; \
|
||||
PSHUFB c40, v3; \
|
||||
MOVO v1, t0; \
|
||||
PMULULQ v3, t0; \
|
||||
PADDQ v3, v1; \
|
||||
PADDQ t0, v1; \
|
||||
PADDQ t0, v1; \
|
||||
PXOR v1, v7; \
|
||||
PSHUFB c48, v7; \
|
||||
MOVO v5, t0; \
|
||||
PMULULQ v7, t0; \
|
||||
PADDQ v7, v5; \
|
||||
PADDQ t0, v5; \
|
||||
PADDQ t0, v5; \
|
||||
PXOR v5, v3; \
|
||||
MOVO v3, t0; \
|
||||
PADDQ v3, t0; \
|
||||
PSRLQ $63, v3; \
|
||||
PXOR t0, v3
|
||||
|
||||
#define LOAD_MSG_0(block, off) \
|
||||
MOVOU 8*(off+0)(block), X0; \
|
||||
MOVOU 8*(off+2)(block), X1; \
|
||||
MOVOU 8*(off+4)(block), X2; \
|
||||
MOVOU 8*(off+6)(block), X3; \
|
||||
MOVOU 8*(off+8)(block), X4; \
|
||||
MOVOU 8*(off+10)(block), X5; \
|
||||
MOVOU 8*(off+12)(block), X6; \
|
||||
MOVOU 8*(off+14)(block), X7
|
||||
|
||||
#define STORE_MSG_0(block, off) \
|
||||
MOVOU X0, 8*(off+0)(block); \
|
||||
MOVOU X1, 8*(off+2)(block); \
|
||||
MOVOU X2, 8*(off+4)(block); \
|
||||
MOVOU X3, 8*(off+6)(block); \
|
||||
MOVOU X4, 8*(off+8)(block); \
|
||||
MOVOU X5, 8*(off+10)(block); \
|
||||
MOVOU X6, 8*(off+12)(block); \
|
||||
MOVOU X7, 8*(off+14)(block)
|
||||
|
||||
#define LOAD_MSG_1(block, off) \
|
||||
MOVOU 8*off+0*8(block), X0; \
|
||||
MOVOU 8*off+16*8(block), X1; \
|
||||
MOVOU 8*off+32*8(block), X2; \
|
||||
MOVOU 8*off+48*8(block), X3; \
|
||||
MOVOU 8*off+64*8(block), X4; \
|
||||
MOVOU 8*off+80*8(block), X5; \
|
||||
MOVOU 8*off+96*8(block), X6; \
|
||||
MOVOU 8*off+112*8(block), X7
|
||||
|
||||
#define STORE_MSG_1(block, off) \
|
||||
MOVOU X0, 8*off+0*8(block); \
|
||||
MOVOU X1, 8*off+16*8(block); \
|
||||
MOVOU X2, 8*off+32*8(block); \
|
||||
MOVOU X3, 8*off+48*8(block); \
|
||||
MOVOU X4, 8*off+64*8(block); \
|
||||
MOVOU X5, 8*off+80*8(block); \
|
||||
MOVOU X6, 8*off+96*8(block); \
|
||||
MOVOU X7, 8*off+112*8(block)
|
||||
|
||||
#define BLAMKA_ROUND_0(block, off, t0, t1, c40, c48) \
|
||||
LOAD_MSG_0(block, off); \
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48); \
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, t0, t1); \
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48); \
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, t0, t1); \
|
||||
STORE_MSG_0(block, off)
|
||||
|
||||
#define BLAMKA_ROUND_1(block, off, t0, t1, c40, c48) \
|
||||
LOAD_MSG_1(block, off); \
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48); \
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, t0, t1); \
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48); \
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, t0, t1); \
|
||||
STORE_MSG_1(block, off)
|
||||
|
||||
// func blamkaSSE4(b *block)
|
||||
TEXT ·blamkaSSE4(SB), 4, $0-8
|
||||
MOVQ b+0(FP), AX
|
||||
|
||||
MOVOU ·c40<>(SB), X10
|
||||
MOVOU ·c48<>(SB), X11
|
||||
|
||||
BLAMKA_ROUND_0(AX, 0, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_0(AX, 16, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_0(AX, 32, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_0(AX, 48, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_0(AX, 64, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_0(AX, 80, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_0(AX, 96, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_0(AX, 112, X8, X9, X10, X11)
|
||||
|
||||
BLAMKA_ROUND_1(AX, 0, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_1(AX, 2, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_1(AX, 4, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_1(AX, 6, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_1(AX, 8, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_1(AX, 10, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_1(AX, 12, X8, X9, X10, X11)
|
||||
BLAMKA_ROUND_1(AX, 14, X8, X9, X10, X11)
|
||||
RET
|
||||
|
||||
// func mixBlocksSSE2(out, a, b, c *block)
|
||||
TEXT ·mixBlocksSSE2(SB), 4, $0-32
|
||||
MOVQ out+0(FP), DX
|
||||
MOVQ a+8(FP), AX
|
||||
MOVQ b+16(FP), BX
|
||||
MOVQ a+24(FP), CX
|
||||
MOVQ $128, BP
|
||||
|
||||
loop:
|
||||
MOVOU 0(AX), X0
|
||||
MOVOU 0(BX), X1
|
||||
MOVOU 0(CX), X2
|
||||
PXOR X1, X0
|
||||
PXOR X2, X0
|
||||
MOVOU X0, 0(DX)
|
||||
ADDQ $16, AX
|
||||
ADDQ $16, BX
|
||||
ADDQ $16, CX
|
||||
ADDQ $16, DX
|
||||
SUBQ $2, BP
|
||||
JA loop
|
||||
RET
|
||||
|
||||
// func xorBlocksSSE2(out, a, b, c *block)
|
||||
TEXT ·xorBlocksSSE2(SB), 4, $0-32
|
||||
MOVQ out+0(FP), DX
|
||||
MOVQ a+8(FP), AX
|
||||
MOVQ b+16(FP), BX
|
||||
MOVQ a+24(FP), CX
|
||||
MOVQ $128, BP
|
||||
|
||||
loop:
|
||||
MOVOU 0(AX), X0
|
||||
MOVOU 0(BX), X1
|
||||
MOVOU 0(CX), X2
|
||||
MOVOU 0(DX), X3
|
||||
PXOR X1, X0
|
||||
PXOR X2, X0
|
||||
PXOR X3, X0
|
||||
MOVOU X0, 0(DX)
|
||||
ADDQ $16, AX
|
||||
ADDQ $16, BX
|
||||
ADDQ $16, CX
|
||||
ADDQ $16, DX
|
||||
SUBQ $2, BP
|
||||
JA loop
|
||||
RET
|
||||
|
||||
// func supportsSSE4() bool
|
||||
TEXT ·supportsSSE4(SB), 4, $0-1
|
||||
MOVL $1, AX
|
||||
CPUID
|
||||
SHRL $19, CX // Bit 19 indicates SSE4 support
|
||||
ANDL $1, CX // CX != 0 if support SSE4
|
||||
MOVB CX, ret+0(FP)
|
||||
RET
|
|
@ -0,0 +1,163 @@
|
|||
// 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
|
||||
|
||||
var useSSE4 bool
|
||||
|
||||
func processBlockGeneric(out, in1, in2 *block, xor bool) {
|
||||
var t block
|
||||
for i := range t {
|
||||
t[i] = in1[i] ^ in2[i]
|
||||
}
|
||||
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 {
|
||||
for i := range t {
|
||||
out[i] ^= in1[i] ^ in2[i] ^ t[i]
|
||||
}
|
||||
} else {
|
||||
for i := range t {
|
||||
out[i] = in1[i] ^ in2[i] ^ t[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func blamkaGeneric(t00, t01, t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t12, t13, t14, t15 *uint64) {
|
||||
v00, v01, v02, v03 := *t00, *t01, *t02, *t03
|
||||
v04, v05, v06, v07 := *t04, *t05, *t06, *t07
|
||||
v08, v09, v10, v11 := *t08, *t09, *t10, *t11
|
||||
v12, v13, v14, v15 := *t12, *t13, *t14, *t15
|
||||
|
||||
v00 += v04 + 2*uint64(uint32(v00))*uint64(uint32(v04))
|
||||
v12 ^= v00
|
||||
v12 = v12>>32 | v12<<32
|
||||
v08 += v12 + 2*uint64(uint32(v08))*uint64(uint32(v12))
|
||||
v04 ^= v08
|
||||
v04 = v04>>24 | v04<<40
|
||||
|
||||
v00 += v04 + 2*uint64(uint32(v00))*uint64(uint32(v04))
|
||||
v12 ^= v00
|
||||
v12 = v12>>16 | v12<<48
|
||||
v08 += v12 + 2*uint64(uint32(v08))*uint64(uint32(v12))
|
||||
v04 ^= v08
|
||||
v04 = v04>>63 | v04<<1
|
||||
|
||||
v01 += v05 + 2*uint64(uint32(v01))*uint64(uint32(v05))
|
||||
v13 ^= v01
|
||||
v13 = v13>>32 | v13<<32
|
||||
v09 += v13 + 2*uint64(uint32(v09))*uint64(uint32(v13))
|
||||
v05 ^= v09
|
||||
v05 = v05>>24 | v05<<40
|
||||
|
||||
v01 += v05 + 2*uint64(uint32(v01))*uint64(uint32(v05))
|
||||
v13 ^= v01
|
||||
v13 = v13>>16 | v13<<48
|
||||
v09 += v13 + 2*uint64(uint32(v09))*uint64(uint32(v13))
|
||||
v05 ^= v09
|
||||
v05 = v05>>63 | v05<<1
|
||||
|
||||
v02 += v06 + 2*uint64(uint32(v02))*uint64(uint32(v06))
|
||||
v14 ^= v02
|
||||
v14 = v14>>32 | v14<<32
|
||||
v10 += v14 + 2*uint64(uint32(v10))*uint64(uint32(v14))
|
||||
v06 ^= v10
|
||||
v06 = v06>>24 | v06<<40
|
||||
|
||||
v02 += v06 + 2*uint64(uint32(v02))*uint64(uint32(v06))
|
||||
v14 ^= v02
|
||||
v14 = v14>>16 | v14<<48
|
||||
v10 += v14 + 2*uint64(uint32(v10))*uint64(uint32(v14))
|
||||
v06 ^= v10
|
||||
v06 = v06>>63 | v06<<1
|
||||
|
||||
v03 += v07 + 2*uint64(uint32(v03))*uint64(uint32(v07))
|
||||
v15 ^= v03
|
||||
v15 = v15>>32 | v15<<32
|
||||
v11 += v15 + 2*uint64(uint32(v11))*uint64(uint32(v15))
|
||||
v07 ^= v11
|
||||
v07 = v07>>24 | v07<<40
|
||||
|
||||
v03 += v07 + 2*uint64(uint32(v03))*uint64(uint32(v07))
|
||||
v15 ^= v03
|
||||
v15 = v15>>16 | v15<<48
|
||||
v11 += v15 + 2*uint64(uint32(v11))*uint64(uint32(v15))
|
||||
v07 ^= v11
|
||||
v07 = v07>>63 | v07<<1
|
||||
|
||||
v00 += v05 + 2*uint64(uint32(v00))*uint64(uint32(v05))
|
||||
v15 ^= v00
|
||||
v15 = v15>>32 | v15<<32
|
||||
v10 += v15 + 2*uint64(uint32(v10))*uint64(uint32(v15))
|
||||
v05 ^= v10
|
||||
v05 = v05>>24 | v05<<40
|
||||
|
||||
v00 += v05 + 2*uint64(uint32(v00))*uint64(uint32(v05))
|
||||
v15 ^= v00
|
||||
v15 = v15>>16 | v15<<48
|
||||
v10 += v15 + 2*uint64(uint32(v10))*uint64(uint32(v15))
|
||||
v05 ^= v10
|
||||
v05 = v05>>63 | v05<<1
|
||||
|
||||
v01 += v06 + 2*uint64(uint32(v01))*uint64(uint32(v06))
|
||||
v12 ^= v01
|
||||
v12 = v12>>32 | v12<<32
|
||||
v11 += v12 + 2*uint64(uint32(v11))*uint64(uint32(v12))
|
||||
v06 ^= v11
|
||||
v06 = v06>>24 | v06<<40
|
||||
|
||||
v01 += v06 + 2*uint64(uint32(v01))*uint64(uint32(v06))
|
||||
v12 ^= v01
|
||||
v12 = v12>>16 | v12<<48
|
||||
v11 += v12 + 2*uint64(uint32(v11))*uint64(uint32(v12))
|
||||
v06 ^= v11
|
||||
v06 = v06>>63 | v06<<1
|
||||
|
||||
v02 += v07 + 2*uint64(uint32(v02))*uint64(uint32(v07))
|
||||
v13 ^= v02
|
||||
v13 = v13>>32 | v13<<32
|
||||
v08 += v13 + 2*uint64(uint32(v08))*uint64(uint32(v13))
|
||||
v07 ^= v08
|
||||
v07 = v07>>24 | v07<<40
|
||||
|
||||
v02 += v07 + 2*uint64(uint32(v02))*uint64(uint32(v07))
|
||||
v13 ^= v02
|
||||
v13 = v13>>16 | v13<<48
|
||||
v08 += v13 + 2*uint64(uint32(v08))*uint64(uint32(v13))
|
||||
v07 ^= v08
|
||||
v07 = v07>>63 | v07<<1
|
||||
|
||||
v03 += v04 + 2*uint64(uint32(v03))*uint64(uint32(v04))
|
||||
v14 ^= v03
|
||||
v14 = v14>>32 | v14<<32
|
||||
v09 += v14 + 2*uint64(uint32(v09))*uint64(uint32(v14))
|
||||
v04 ^= v09
|
||||
v04 = v04>>24 | v04<<40
|
||||
|
||||
v03 += v04 + 2*uint64(uint32(v03))*uint64(uint32(v04))
|
||||
v14 ^= v03
|
||||
v14 = v14>>16 | v14<<48
|
||||
v09 += v14 + 2*uint64(uint32(v09))*uint64(uint32(v14))
|
||||
v04 ^= v09
|
||||
v04 = v04>>63 | v04<<1
|
||||
|
||||
*t00, *t01, *t02, *t03 = v00, v01, v02, v03
|
||||
*t04, *t05, *t06, *t07 = v04, v05, v06, v07
|
||||
*t08, *t09, *t10, *t11 = v08, v09, v10, v11
|
||||
*t12, *t13, *t14, *t15 = v12, v13, v14, v15
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// 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 appengine gccgo
|
||||
|
||||
package argon2
|
||||
|
||||
func processBlock(out, in1, in2 *block) {
|
||||
processBlockGeneric(out, in1, in2, false)
|
||||
}
|
||||
|
||||
func processBlockXOR(out, in1, in2 *block) {
|
||||
processBlockGeneric(out, in1, in2, true)
|
||||
}
|
|
@ -39,7 +39,10 @@ var (
|
|||
useSSE4 bool
|
||||
)
|
||||
|
||||
var errKeySize = errors.New("blake2b: invalid key size")
|
||||
var (
|
||||
errKeySize = errors.New("blake2b: invalid key size")
|
||||
errHashSize = errors.New("blake2b: invalid hash size")
|
||||
)
|
||||
|
||||
var iv = [8]uint64{
|
||||
0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
|
||||
|
@ -83,7 +86,18 @@ func New384(key []byte) (hash.Hash, error) { return newDigest(Size384, key) }
|
|||
// key turns the hash into a MAC. The key must between zero and 64 bytes long.
|
||||
func New256(key []byte) (hash.Hash, error) { return newDigest(Size256, key) }
|
||||
|
||||
// New returns a new hash.Hash computing the BLAKE2b checksum with a custom length.
|
||||
// A non-nil key turns the hash into a MAC. The key must between zero and 64 bytes long.
|
||||
// The hash size can be a value between 1 and 64 but it is highly recommended to use
|
||||
// values equal or greater than:
|
||||
// - 32 if BLAKE2b is used as a hash function (The key is zero bytes long).
|
||||
// - 16 if BLAKE2b is used as a MAC function (The key is at least 16 bytes long).
|
||||
func New(size int, key []byte) (hash.Hash, error) { return newDigest(size, key) }
|
||||
|
||||
func newDigest(hashSize int, key []byte) (*digest, error) {
|
||||
if hashSize < 1 || hashSize > Size {
|
||||
return nil, errHashSize
|
||||
}
|
||||
if len(key) > Size {
|
||||
return nil, errKeySize
|
||||
}
|
||||
|
|
|
@ -155,3 +155,22 @@ func TestWithHMACSHA1(t *testing.T) {
|
|||
func TestWithHMACSHA256(t *testing.T) {
|
||||
testHash(t, sha256.New, "SHA256", sha256TestVectors)
|
||||
}
|
||||
|
||||
var sink uint8
|
||||
|
||||
func benchmark(b *testing.B, h func() hash.Hash) {
|
||||
password := make([]byte, h().Size())
|
||||
salt := make([]byte, 8)
|
||||
for i := 0; i < b.N; i++ {
|
||||
password = Key(password, salt, 4096, len(password), h)
|
||||
}
|
||||
sink += password[0]
|
||||
}
|
||||
|
||||
func BenchmarkHMACSHA1(b *testing.B) {
|
||||
benchmark(b, sha1.New)
|
||||
}
|
||||
|
||||
func BenchmarkHMACSHA256(b *testing.B) {
|
||||
benchmark(b, sha256.New)
|
||||
}
|
||||
|
|
|
@ -6,10 +6,15 @@ package ssh
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh/testdata"
|
||||
)
|
||||
|
||||
// Cert generated by ssh-keygen 6.0p1 Debian-4.
|
||||
|
@ -220,3 +225,111 @@ func TestHostKeyCert(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCertTypes(t *testing.T) {
|
||||
var testVars = []struct {
|
||||
name string
|
||||
keys func() Signer
|
||||
}{
|
||||
{
|
||||
name: CertAlgoECDSA256v01,
|
||||
keys: func() Signer {
|
||||
s, _ := ParsePrivateKey(testdata.PEMBytes["ecdsap256"])
|
||||
return s
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CertAlgoECDSA384v01,
|
||||
keys: func() Signer {
|
||||
s, _ := ParsePrivateKey(testdata.PEMBytes["ecdsap384"])
|
||||
return s
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CertAlgoECDSA521v01,
|
||||
keys: func() Signer {
|
||||
s, _ := ParsePrivateKey(testdata.PEMBytes["ecdsap521"])
|
||||
return s
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CertAlgoED25519v01,
|
||||
keys: func() Signer {
|
||||
s, _ := ParsePrivateKey(testdata.PEMBytes["ed25519"])
|
||||
return s
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CertAlgoRSAv01,
|
||||
keys: func() Signer {
|
||||
s, _ := ParsePrivateKey(testdata.PEMBytes["rsa"])
|
||||
return s
|
||||
},
|
||||
},
|
||||
{
|
||||
name: CertAlgoDSAv01,
|
||||
keys: func() Signer {
|
||||
s, _ := ParsePrivateKey(testdata.PEMBytes["dsa"])
|
||||
return s
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("error generating host key: %v", err)
|
||||
}
|
||||
|
||||
signer, err := NewSignerFromKey(k)
|
||||
if err != nil {
|
||||
t.Fatalf("error generating signer for ssh listener: %v", err)
|
||||
}
|
||||
|
||||
conf := &ServerConfig{
|
||||
PublicKeyCallback: func(c ConnMetadata, k PublicKey) (*Permissions, error) {
|
||||
return new(Permissions), nil
|
||||
},
|
||||
}
|
||||
conf.AddHostKey(signer)
|
||||
|
||||
for _, m := range testVars {
|
||||
t.Run(m.name, func(t *testing.T) {
|
||||
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
go NewServerConn(c1, conf)
|
||||
|
||||
priv := m.keys()
|
||||
if err != nil {
|
||||
t.Fatalf("error generating ssh pubkey: %v", err)
|
||||
}
|
||||
|
||||
cert := &Certificate{
|
||||
CertType: UserCert,
|
||||
Key: priv.PublicKey(),
|
||||
}
|
||||
cert.SignCert(rand.Reader, priv)
|
||||
|
||||
certSigner, err := NewCertSigner(cert, priv)
|
||||
if err != nil {
|
||||
t.Fatalf("error generating cert signer: %v", err)
|
||||
}
|
||||
|
||||
config := &ClientConfig{
|
||||
User: "user",
|
||||
HostKeyCallback: func(h string, r net.Addr, k PublicKey) error { return nil },
|
||||
Auth: []AuthMethod{PublicKeys(certSigner)},
|
||||
}
|
||||
|
||||
_, _, _, err = NewClientConn(c2, "", config)
|
||||
if err != nil {
|
||||
t.Fatalf("error connecting: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -256,7 +256,7 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)
|
|||
func isAcceptableAlgo(algo string) bool {
|
||||
switch algo {
|
||||
case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoED25519,
|
||||
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01:
|
||||
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
|
@ -23,6 +23,27 @@ MHcCAQEEINGWx0zo6fhJ/0EAfrPzVFyFC9s18lBt3cRoEDhS3ARooAoGCCqGSM49
|
|||
AwEHoUQDQgAEi9Hdw6KvZcWxfg2IDhA7UkpDtzzt6ZqJXSsFdLd+Kx4S3Sx4cVO+
|
||||
6/ZOXRnPmNAlLUqjShUsUBBngG0u2fqEqA==
|
||||
-----END EC PRIVATE KEY-----
|
||||
`),
|
||||
"ecdsap256": []byte(`-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIAPCE25zK0PQSnsgVcEbM1mbKTASH4pqb5QJajplDwDZoAoGCCqGSM49
|
||||
AwEHoUQDQgAEWy8TxGcIHRh5XGpO4dFVfDjeNY+VkgubQrf/eyFJZHxAn1SKraXU
|
||||
qJUjTKj1z622OxYtJ5P7s9CfAEVsTzLCzg==
|
||||
-----END EC PRIVATE KEY-----
|
||||
`),
|
||||
"ecdsap384": []byte(`-----BEGIN EC PRIVATE KEY-----
|
||||
MIGkAgEBBDBWfSnMuNKq8J9rQLzzEkx3KAoEohSXqhE/4CdjEYtoU2i22HW80DDS
|
||||
qQhYNHRAduygBwYFK4EEACKhZANiAAQWaDMAd0HUd8ZiXCX7mYDDnC54gwH/nG43
|
||||
VhCUEYmF7HMZm/B9Yn3GjFk3qYEDEvuF/52+NvUKBKKaLbh32AWxMv0ibcoba4cz
|
||||
hL9+hWYhUD9XIUlzMWiZ2y6eBE9PdRI=
|
||||
-----END EC PRIVATE KEY-----
|
||||
`),
|
||||
"ecdsap521": []byte(`-----BEGIN EC PRIVATE KEY-----
|
||||
MIHcAgEBBEIBrkYpQcy8KTVHNiAkjlFZwee90224Bu6wz94R4OBo+Ts0eoAQG7SF
|
||||
iaygEDMUbx6kTgXTBcKZ0jrWPKakayNZ/kigBwYFK4EEACOhgYkDgYYABADFuvLV
|
||||
UoaCDGHcw5uNfdRIsvaLKuWSpLsl48eWGZAwdNG432GDVKduO+pceuE+8XzcyJb+
|
||||
uMv+D2b11Q/LQUcHJwE6fqbm8m3EtDKPsoKs0u/XUJb0JsH4J8lkZzbUTjvGYamn
|
||||
FFlRjzoB3Oxu8UQgb+MWPedtH9XYBbg9biz4jJLkXQ==
|
||||
-----END EC PRIVATE KEY-----
|
||||
`),
|
||||
"rsa": []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXAIBAAKBgQC8A6FGHDiWCSREAXCq6yBfNVr0xCVG2CzvktFNRpue+RXrGs/2
|
||||
|
|
|
@ -110,7 +110,7 @@ func ControlMessageSpace(dataLen int) int {
|
|||
type ControlMessage []byte
|
||||
|
||||
// Data returns the data field of the control message at the head on
|
||||
// w.
|
||||
// m.
|
||||
func (m ControlMessage) Data(dataLen int) []byte {
|
||||
l := controlHeaderLen()
|
||||
if len(m) < l || len(m) < l+dataLen {
|
||||
|
@ -119,7 +119,7 @@ func (m ControlMessage) Data(dataLen int) []byte {
|
|||
return m[l : l+dataLen]
|
||||
}
|
||||
|
||||
// Next returns the control message at the next on w.
|
||||
// Next returns the control message at the next on m.
|
||||
//
|
||||
// Next works only for standard control messages.
|
||||
func (m ControlMessage) Next(dataLen int) ControlMessage {
|
||||
|
@ -131,7 +131,7 @@ func (m ControlMessage) Next(dataLen int) ControlMessage {
|
|||
}
|
||||
|
||||
// MarshalHeader marshals the header fields of the control message at
|
||||
// the head on w.
|
||||
// the head on m.
|
||||
func (m ControlMessage) MarshalHeader(lvl, typ, dataLen int) error {
|
||||
if len(m) < controlHeaderLen() {
|
||||
return errors.New("short message")
|
||||
|
@ -142,7 +142,7 @@ func (m ControlMessage) MarshalHeader(lvl, typ, dataLen int) error {
|
|||
}
|
||||
|
||||
// ParseHeader parses and returns the header fields of the control
|
||||
// message at the head on w.
|
||||
// message at the head on m.
|
||||
func (m ControlMessage) ParseHeader() (lvl, typ, dataLen int, err error) {
|
||||
l := controlHeaderLen()
|
||||
if len(m) < l {
|
||||
|
@ -152,7 +152,7 @@ func (m ControlMessage) ParseHeader() (lvl, typ, dataLen int, err error) {
|
|||
return h.lvl(), h.typ(), int(uint64(h.len()) - uint64(l)), nil
|
||||
}
|
||||
|
||||
// Marshal marshals the control message at the head on w, and returns
|
||||
// Marshal marshals the control message at the head on m, and returns
|
||||
// the next control message.
|
||||
func (m ControlMessage) Marshal(lvl, typ int, data []byte) (ControlMessage, error) {
|
||||
l := len(data)
|
||||
|
@ -167,7 +167,7 @@ func (m ControlMessage) Marshal(lvl, typ int, data []byte) (ControlMessage, erro
|
|||
return m.Next(l), nil
|
||||
}
|
||||
|
||||
// Parse parses w as a single or multiple control messages.
|
||||
// Parse parses m as a single or multiple control messages.
|
||||
//
|
||||
// Parse works for both standard and compatible messages.
|
||||
func (m ControlMessage) Parse() ([]ControlMessage, error) {
|
||||
|
|
Loading…
Reference in New Issue