瀏覽代碼

support migrating anope databases

tags/v2.4.0-rc1
Shivaram Lingamneni 3 年之前
父節點
當前提交
82be9a8423

+ 165
- 0
distrib/anope/anope2json.py 查看文件

1
+#!/usr/bin/python3
2
+
3
+import re
4
+import json
5
+import logging
6
+import sys
7
+from collections import defaultdict, namedtuple
8
+
9
+AnopeObject = namedtuple('AnopeObject', ('type', 'kv'))
10
+
11
+MASK_MAGIC_REGEX = re.compile(r'[*?!@]')
12
+
13
+def access_level_to_amode(level):
14
+    try:
15
+        level = int(level)
16
+    except:
17
+        return None
18
+    if level >= 10000:
19
+        return 'q'
20
+    elif level >= 9999:
21
+        return 'a'
22
+    elif level >= 5:
23
+        return 'o'
24
+    elif level >= 4:
25
+        return 'h'
26
+    elif level >= 3:
27
+        return 'v'
28
+    else:
29
+        return None
30
+
31
+def to_unixnano(timestamp):
32
+    return int(timestamp) * (10**9)
33
+
34
+def file_to_objects(infile):
35
+    result = []
36
+    obj = None
37
+    for line in infile:
38
+        pieces = line.rstrip('\r\n').split(' ', maxsplit=2)
39
+        if len(pieces) == 0:
40
+            logging.warning("skipping blank line in db")
41
+            continue
42
+        if pieces[0] == 'END':
43
+            result.append(obj)
44
+            obj = None
45
+        elif pieces[0] == 'OBJECT':
46
+            obj = AnopeObject(pieces[1], {})
47
+        elif pieces[0] == 'DATA':
48
+            obj.kv[pieces[1]] = pieces[2]
49
+        else:
50
+            raise ValueError("unknown command found in anope db", pieces[0])
51
+    return result
52
+
53
+ANOPE_MODENAME_TO_MODE = {
54
+    'NOEXTERNAL': 'n',
55
+    'TOPIC': 't',
56
+    'INVITE': 'i',
57
+    'NOCTCP': 'C',
58
+    'AUDITORIUM': 'u',
59
+    'SECRET': 's',
60
+}
61
+
62
+def convert(infile):
63
+    out = {
64
+        'version': 1,
65
+        'source': 'anope',
66
+        'users': defaultdict(dict),
67
+        'channels': defaultdict(dict),
68
+    }
69
+
70
+    objects = file_to_objects(infile)
71
+
72
+    lastmode_channels = set()
73
+
74
+    for obj in objects:
75
+        if obj.type == 'NickCore':
76
+            username = obj.kv['display']
77
+            userdata = {'name': username, 'hash': obj.kv['pass'], 'email': obj.kv['email']}
78
+            out['users'][username] = userdata
79
+        elif obj.type == 'NickAlias':
80
+            username = obj.kv['nc']
81
+            nick = obj.kv['nick']
82
+            userdata = out['users'][username]
83
+            if username.lower() == nick.lower():
84
+                userdata['registeredAt'] = to_unixnano(obj.kv['time_registered'])
85
+            else:
86
+                if 'additionalNicks' not in userdata:
87
+                    userdata['additionalNicks'] = []
88
+                userdata['additionalNicks'].append(nick)
89
+        elif obj.type == 'ChannelInfo':
90
+            chname = obj.kv['name']
91
+            founder = obj.kv['founder']
92
+            chdata = {
93
+                'name': chname,
94
+                'founder': founder,
95
+                'registeredAt': to_unixnano(obj.kv['time_registered']),
96
+                'topic': obj.kv['last_topic'],
97
+                'topicSetBy': obj.kv['last_topic_setter'],
98
+                'topicSetAt': to_unixnano(obj.kv['last_topic_time']),
99
+                'amode': {founder: 'q',}
100
+            }
101
+            # DATA last_modes INVITE KEY,hunter2 NOEXTERNAL REGISTERED TOPIC
102
+            last_modes = obj.kv.get('last_modes')
103
+            if last_modes:
104
+                modes = []
105
+                for mode_desc in last_modes.split():
106
+                    if ',' in mode_desc:
107
+                        mode_name, mode_value = mode_desc.split(',', maxsplit=1)
108
+                    else:
109
+                        mode_name, mode_value = mode_desc, None
110
+                    if mode_name == 'KEY':
111
+                        chdata['key'] = mode_value
112
+                    else:
113
+                        modes.append(ANOPE_MODENAME_TO_MODE.get(mode_name, ''))
114
+                chdata['modes'] = ''.join(modes)
115
+                # prevent subsequent ModeLock objects from modifying the mode list further:
116
+                lastmode_channels.add(chname)
117
+            out['channels'][chname] = chdata
118
+        elif obj.type == 'ModeLock':
119
+            if obj.kv.get('set') != '1':
120
+                continue
121
+            chname = obj.kv['ci']
122
+            if chname in lastmode_channels:
123
+                continue
124
+            chdata = out['channels'][chname]
125
+            modename = obj.kv['name']
126
+            if modename == 'KEY':
127
+                chdata['key'] = obj.kv['param']
128
+            else:
129
+                oragono_mode = ANOPE_MODENAME_TO_MODE.get(modename)
130
+                if oragono_mode is not None:
131
+                    stored_modes = chdata.get('modes', '')
132
+                    stored_modes += oragono_mode
133
+                    chdata['modes'] = stored_modes
134
+        elif obj.type == 'ChanAccess':
135
+            chname = obj.kv['ci']
136
+            target = obj.kv['mask']
137
+            mode = access_level_to_amode(obj.kv['data'])
138
+            if mode is None:
139
+                continue
140
+            if MASK_MAGIC_REGEX.search(target):
141
+                continue
142
+            chdata = out['channels'][chname]
143
+            amode = chdata.setdefault('amode', {})
144
+            amode[target] = mode
145
+            chdata['amode'] = amode
146
+
147
+    # do some basic integrity checks
148
+    for chname, chdata in out['channels'].items():
149
+        founder = chdata.get('founder')
150
+        if founder not in out['users']:
151
+            raise ValueError("no user corresponding to channel founder", chname, chdata.get('founder'))
152
+
153
+    return out
154
+
155
+def main():
156
+    if len(sys.argv) != 3:
157
+        raise Exception("Usage: anope2json.py anope.db output.json")
158
+    with open(sys.argv[1]) as infile:
159
+        output = convert(infile)
160
+        with open(sys.argv[2], 'w') as outfile:
161
+            json.dump(output, outfile)
162
+
163
+if __name__ == '__main__':
164
+    logging.basicConfig()
165
+    sys.exit(main())

+ 38
- 15
distrib/atheme/atheme2json.py 查看文件

1
+#!/usr/bin/python3
2
+
1
 import json
3
 import json
2
 import logging
4
 import logging
3
 import sys
5
 import sys
6
 def to_unixnano(timestamp):
8
 def to_unixnano(timestamp):
7
     return int(timestamp) * (10**9)
9
     return int(timestamp) * (10**9)
8
 
10
 
11
+# include/atheme/channels.h
12
+CMODE_FLAG_TO_MODE = {
13
+    0x001: 'i', # CMODE_INVITE
14
+    0x010: 'n', # CMODE_NOEXT
15
+    0x080: 's', # CMODE_SEC
16
+    0x100: 't', # CMODE_TOPIC
17
+}
18
+
9
 def convert(infile):
19
 def convert(infile):
10
     out = {
20
     out = {
11
         'version': 1,
21
         'version': 1,
17
     channel_to_founder = defaultdict(lambda: (None, None))
27
     channel_to_founder = defaultdict(lambda: (None, None))
18
 
28
 
19
     for line in infile:
29
     for line in infile:
20
-        line = line.strip()
21
-        parts = line.split()
30
+        line = line.rstrip('\r\n')
31
+        parts = line.split(' ')
22
         category = parts[0]
32
         category = parts[0]
23
         if category == 'MU':
33
         if category == 'MU':
24
             # user account
34
             # user account
43
         elif category == 'MC':
53
         elif category == 'MC':
44
             # channel registration
54
             # channel registration
45
             # MC #mychannel 1600134478 1600467343 +v 272 0 0
55
             # MC #mychannel 1600134478 1600467343 +v 272 0 0
56
+            # MC #NEWCHANNELTEST 1602270889 1602270974 +vg 1 0 0 jaeger4
46
             chname = parts[1]
57
             chname = parts[1]
47
-            out['channels'][chname].update({'name': chname, 'registeredAt': to_unixnano(parts[2])})
58
+            chdata = out['channels'][chname]
59
+            # XXX just give everyone +nt, regardless of lock status; they can fix it later
60
+            chdata.update({'name': chname, 'registeredAt': to_unixnano(parts[2])})
61
+            if parts[8] != '':
62
+                chdata['key'] = parts[8]
63
+            modes = {'n', 't'}
64
+            mlock_on, mlock_off = int(parts[5]), int(parts[6])
65
+            for flag, mode in CMODE_FLAG_TO_MODE.items():
66
+                if flag & mlock_on != 0:
67
+                    modes.add(mode)
68
+            for flag, mode in CMODE_FLAG_TO_MODE.items():
69
+                if flag & mlock_off != 0:
70
+                    modes.remove(mode)
71
+            chdata['modes'] = ''.join(modes)
72
+            chdata['limit'] = int(parts[7])
48
         elif category == 'MDC':
73
         elif category == 'MDC':
49
             # auxiliary data for a channel registration
74
             # auxiliary data for a channel registration
50
             # MDC #mychannel private:topic:setter s
75
             # MDC #mychannel private:topic:setter s
68
             set_at = int(parts[4])
93
             set_at = int(parts[4])
69
             if 'amode' not in chdata:
94
             if 'amode' not in chdata:
70
                 chdata['amode'] = {}
95
                 chdata['amode'] = {}
96
+            # see libathemecore/flags.c: +o is op, +O is autoop, etc.
71
             if 'F' in flags:
97
             if 'F' in flags:
72
                 # there can only be one founder
98
                 # there can only be one founder
73
                 preexisting_founder, preexisting_set_at = channel_to_founder[chname]
99
                 preexisting_founder, preexisting_set_at = channel_to_founder[chname]
75
                     chdata['founder'] = username
101
                     chdata['founder'] = username
76
                     channel_to_founder[chname] = (username, set_at)
102
                     channel_to_founder[chname] = (username, set_at)
77
                 # but multiple people can receive the 'q' amode
103
                 # but multiple people can receive the 'q' amode
78
-                chdata['amode'][username] = ord('q')
79
-            elif 'a' in flags:
80
-                chdata['amode'][username] = ord('a')
81
-            elif 'o' in flags:
82
-                chdata['amode'][username] = ord('o')
83
-            elif 'h' in flags:
84
-                chdata['amode'][username] = ord('h')
85
-            elif 'v' in flags:
86
-                chdata['amode'][username] = ord('v')
104
+                chdata['amode'][username] = 'q'
105
+            elif 'q' in flags:
106
+                chdata['amode'][username] = 'q'
107
+            elif 'o' in flags or 'O' in flags:
108
+                chdata['amode'][username] = 'o'
109
+            elif 'h' in flags or 'H' in flags:
110
+                chdata['amode'][username] = 'h'
111
+            elif 'v' in flags or 'V' in flags:
112
+                chdata['amode'][username] = 'v'
87
         else:
113
         else:
88
             pass
114
             pass
89
 
115
 
92
         founder = chdata.get('founder')
118
         founder = chdata.get('founder')
93
         if founder not in out['users']:
119
         if founder not in out['users']:
94
             raise ValueError("no user corresponding to channel founder", chname, chdata.get('founder'))
120
             raise ValueError("no user corresponding to channel founder", chname, chdata.get('founder'))
95
-        if 'registeredChannels' not in out['users'][founder]:
96
-            out['users'][founder]['registeredChannels'] = []
97
-        out['users'][founder]['registeredChannels'].append(chname)
98
 
121
 
99
     return out
122
     return out
100
 
123
 

+ 3
- 0
irc/accounts.go 查看文件

1056
 		}
1056
 		}
1057
 	case -1:
1057
 	case -1:
1058
 		err = am.checkLegacyPassphrase(migrations.CheckAthemePassphrase, accountName, account.Credentials.PassphraseHash, passphrase)
1058
 		err = am.checkLegacyPassphrase(migrations.CheckAthemePassphrase, accountName, account.Credentials.PassphraseHash, passphrase)
1059
+	case -2:
1060
+		err = am.checkLegacyPassphrase(migrations.CheckAnopePassphrase, accountName, account.Credentials.PassphraseHash, passphrase)
1059
 	default:
1061
 	default:
1060
 		err = errAccountInvalidCredentials
1062
 		err = errAccountInvalidCredentials
1061
 	}
1063
 	}
1899
 	CredentialsSHA3Bcrypt CredentialsVersion = 1
1901
 	CredentialsSHA3Bcrypt CredentialsVersion = 1
1900
 	// negative numbers for migration
1902
 	// negative numbers for migration
1901
 	CredentialsAtheme = -1
1903
 	CredentialsAtheme = -1
1904
+	CredentialsAnope  = -2
1902
 )
1905
 )
1903
 
1906
 
1904
 // AccountCredentials stores the various methods for verifying accounts.
1907
 // AccountCredentials stores the various methods for verifying accounts.

+ 2
- 5
irc/channelreg.go 查看文件

358
 
358
 
359
 	if includeFlags&IncludeModes != 0 {
359
 	if includeFlags&IncludeModes != 0 {
360
 		tx.Set(fmt.Sprintf(keyChannelPassword, channelKey), channelInfo.Key, nil)
360
 		tx.Set(fmt.Sprintf(keyChannelPassword, channelKey), channelInfo.Key, nil)
361
-		modeStrings := make([]string, len(channelInfo.Modes))
362
-		for i, mode := range channelInfo.Modes {
363
-			modeStrings[i] = string(mode)
364
-		}
365
-		tx.Set(fmt.Sprintf(keyChannelModes, channelKey), strings.Join(modeStrings, ""), nil)
361
+		modeString := modes.Modes(channelInfo.Modes).String()
362
+		tx.Set(fmt.Sprintf(keyChannelModes, channelKey), modeString, nil)
366
 		tx.Set(fmt.Sprintf(keyChannelUserLimit, channelKey), strconv.Itoa(channelInfo.UserLimit), nil)
363
 		tx.Set(fmt.Sprintf(keyChannelUserLimit, channelKey), strconv.Itoa(channelInfo.UserLimit), nil)
367
 	}
364
 	}
368
 
365
 

+ 54
- 18
irc/import.go 查看文件

9
 	"io/ioutil"
9
 	"io/ioutil"
10
 	"log"
10
 	"log"
11
 	"strconv"
11
 	"strconv"
12
-	"strings"
13
 
12
 
14
 	"github.com/tidwall/buntdb"
13
 	"github.com/tidwall/buntdb"
15
 
14
 
17
 )
16
 )
18
 
17
 
19
 type userImport struct {
18
 type userImport struct {
20
-	Name               string
21
-	Hash               string
22
-	Email              string
23
-	RegisteredAt       int64 `json:"registeredAt"`
24
-	Vhost              string
25
-	AdditionalNicks    []string `json:"additionalNicks"`
26
-	RegisteredChannels []string
19
+	Name            string
20
+	Hash            string
21
+	Email           string
22
+	RegisteredAt    int64 `json:"registeredAt"`
23
+	Vhost           string
24
+	AdditionalNicks []string `json:"additionalNicks"`
27
 }
25
 }
28
 
26
 
29
 type channelImport struct {
27
 type channelImport struct {
33
 	Topic        string
31
 	Topic        string
34
 	TopicSetBy   string `json:"topicSetBy"`
32
 	TopicSetBy   string `json:"topicSetBy"`
35
 	TopicSetAt   int64  `json:"topicSetAt"`
33
 	TopicSetAt   int64  `json:"topicSetAt"`
36
-	Amode        map[string]int
34
+	Amode        map[string]string
35
+	Modes        string
36
+	Key          string
37
+	Limit        int
37
 }
38
 }
38
 
39
 
39
 type databaseImport struct {
40
 type databaseImport struct {
43
 	Channels map[string]channelImport
44
 	Channels map[string]channelImport
44
 }
45
 }
45
 
46
 
46
-func doImportAthemeDB(config *Config, dbImport databaseImport, tx *buntdb.Tx) (err error) {
47
+func serializeAmodes(raw map[string]string) (result []byte, err error) {
48
+	processed := make(map[string]int, len(raw))
49
+	for accountName, mode := range raw {
50
+		if len(mode) != 1 {
51
+			return nil, fmt.Errorf("invalid mode %s for account %s", mode, accountName)
52
+		}
53
+		cfname, err := CasefoldName(accountName)
54
+		if err != nil {
55
+			return nil, fmt.Errorf("invalid amode recipient %s: %w", accountName, err)
56
+		}
57
+		processed[cfname] = int(mode[0])
58
+	}
59
+	result, err = json.Marshal(processed)
60
+	return
61
+}
62
+
63
+func doImportDBGeneric(config *Config, dbImport databaseImport, credsType CredentialsVersion, tx *buntdb.Tx) (err error) {
47
 	requiredVersion := 1
64
 	requiredVersion := 1
48
 	if dbImport.Version != requiredVersion {
65
 	if dbImport.Version != requiredVersion {
49
 		return fmt.Errorf("unsupported version of the db for import: version %d is required", requiredVersion)
66
 		return fmt.Errorf("unsupported version of the db for import: version %d is required", requiredVersion)
63
 			continue
80
 			continue
64
 		}
81
 		}
65
 		credentials := AccountCredentials{
82
 		credentials := AccountCredentials{
66
-			Version:        CredentialsAtheme,
83
+			Version:        credsType,
67
 			PassphraseHash: []byte(userInfo.Hash),
84
 			PassphraseHash: []byte(userInfo.Hash),
68
 		}
85
 		}
69
 		marshaledCredentials, err := json.Marshal(&credentials)
86
 		marshaledCredentials, err := json.Marshal(&credentials)
83
 		if len(userInfo.AdditionalNicks) != 0 {
100
 		if len(userInfo.AdditionalNicks) != 0 {
84
 			tx.Set(fmt.Sprintf(keyAccountAdditionalNicks, cfUsername), marshalReservedNicks(userInfo.AdditionalNicks), nil)
101
 			tx.Set(fmt.Sprintf(keyAccountAdditionalNicks, cfUsername), marshalReservedNicks(userInfo.AdditionalNicks), nil)
85
 		}
102
 		}
86
-		if len(userInfo.RegisteredChannels) != 0 {
87
-			tx.Set(fmt.Sprintf(keyAccountChannels, cfUsername), strings.Join(userInfo.RegisteredChannels, ","), nil)
88
-		}
89
 	}
103
 	}
90
 
104
 
91
 	for chname, chInfo := range dbImport.Channels {
105
 	for chname, chInfo := range dbImport.Channels {
94
 			log.Printf("invalid channel name %s: %v", chname, err)
108
 			log.Printf("invalid channel name %s: %v", chname, err)
95
 			continue
109
 			continue
96
 		}
110
 		}
111
+		cffounder, err := CasefoldName(chInfo.Founder)
112
+		if err != nil {
113
+			log.Printf("invalid founder %s for channel %s: %v", chInfo.Founder, chname, err)
114
+			continue
115
+		}
97
 		tx.Set(fmt.Sprintf(keyChannelExists, cfchname), "1", nil)
116
 		tx.Set(fmt.Sprintf(keyChannelExists, cfchname), "1", nil)
98
 		tx.Set(fmt.Sprintf(keyChannelName, cfchname), chname, nil)
117
 		tx.Set(fmt.Sprintf(keyChannelName, cfchname), chname, nil)
99
 		tx.Set(fmt.Sprintf(keyChannelRegTime, cfchname), strconv.FormatInt(chInfo.RegisteredAt, 10), nil)
118
 		tx.Set(fmt.Sprintf(keyChannelRegTime, cfchname), strconv.FormatInt(chInfo.RegisteredAt, 10), nil)
100
-		tx.Set(fmt.Sprintf(keyChannelFounder, cfchname), chInfo.Founder, nil)
119
+		tx.Set(fmt.Sprintf(keyChannelFounder, cfchname), cffounder, nil)
120
+		accountChannelsKey := fmt.Sprintf(keyAccountChannels, cffounder)
121
+		founderChannels, fcErr := tx.Get(accountChannelsKey)
122
+		if fcErr != nil || founderChannels == "" {
123
+			founderChannels = cfchname
124
+		} else {
125
+			founderChannels = fmt.Sprintf("%s,%s", founderChannels, cfchname)
126
+		}
127
+		tx.Set(accountChannelsKey, founderChannels, nil)
101
 		if chInfo.Topic != "" {
128
 		if chInfo.Topic != "" {
102
 			tx.Set(fmt.Sprintf(keyChannelTopic, cfchname), chInfo.Topic, nil)
129
 			tx.Set(fmt.Sprintf(keyChannelTopic, cfchname), chInfo.Topic, nil)
103
 			tx.Set(fmt.Sprintf(keyChannelTopicSetTime, cfchname), strconv.FormatInt(chInfo.TopicSetAt, 10), nil)
130
 			tx.Set(fmt.Sprintf(keyChannelTopicSetTime, cfchname), strconv.FormatInt(chInfo.TopicSetAt, 10), nil)
104
 			tx.Set(fmt.Sprintf(keyChannelTopicSetBy, cfchname), chInfo.TopicSetBy, nil)
131
 			tx.Set(fmt.Sprintf(keyChannelTopicSetBy, cfchname), chInfo.TopicSetBy, nil)
105
 		}
132
 		}
106
 		if len(chInfo.Amode) != 0 {
133
 		if len(chInfo.Amode) != 0 {
107
-			m, err := json.Marshal(chInfo.Amode)
134
+			m, err := serializeAmodes(chInfo.Amode)
108
 			if err == nil {
135
 			if err == nil {
109
 				tx.Set(fmt.Sprintf(keyChannelAccountToUMode, cfchname), string(m), nil)
136
 				tx.Set(fmt.Sprintf(keyChannelAccountToUMode, cfchname), string(m), nil)
110
 			} else {
137
 			} else {
111
 				log.Printf("couldn't serialize amodes for %s: %v", chname, err)
138
 				log.Printf("couldn't serialize amodes for %s: %v", chname, err)
112
 			}
139
 			}
113
 		}
140
 		}
141
+		tx.Set(fmt.Sprintf(keyChannelModes, cfchname), chInfo.Modes, nil)
142
+		if chInfo.Key != "" {
143
+			tx.Set(fmt.Sprintf(keyChannelPassword, cfchname), chInfo.Key, nil)
144
+		}
145
+		if chInfo.Limit > 0 {
146
+			tx.Set(fmt.Sprintf(keyChannelUserLimit, cfchname), strconv.Itoa(chInfo.Limit), nil)
147
+		}
114
 	}
148
 	}
115
 
149
 
116
 	return nil
150
 	return nil
119
 func doImportDB(config *Config, dbImport databaseImport, tx *buntdb.Tx) (err error) {
153
 func doImportDB(config *Config, dbImport databaseImport, tx *buntdb.Tx) (err error) {
120
 	switch dbImport.Source {
154
 	switch dbImport.Source {
121
 	case "atheme":
155
 	case "atheme":
122
-		return doImportAthemeDB(config, dbImport, tx)
156
+		return doImportDBGeneric(config, dbImport, CredentialsAtheme, tx)
157
+	case "anope":
158
+		return doImportDBGeneric(config, dbImport, CredentialsAnope, tx)
123
 	default:
159
 	default:
124
-		return fmt.Errorf("only imports from atheme are currently supported")
160
+		return fmt.Errorf("unsupported import source: %s", dbImport.Source)
125
 	}
161
 	}
126
 }
162
 }
127
 
163
 

+ 100
- 2
irc/migrations/passwords.go 查看文件

9
 	"crypto/sha512"
9
 	"crypto/sha512"
10
 	"crypto/subtle"
10
 	"crypto/subtle"
11
 	"encoding/base64"
11
 	"encoding/base64"
12
+	"encoding/binary"
12
 	"encoding/hex"
13
 	"encoding/hex"
13
 	"errors"
14
 	"errors"
14
 	"hash"
15
 	"hash"
15
 	"strconv"
16
 	"strconv"
16
 
17
 
17
 	"github.com/GehirnInc/crypt/md5_crypt"
18
 	"github.com/GehirnInc/crypt/md5_crypt"
19
+	"golang.org/x/crypto/bcrypt"
18
 	"golang.org/x/crypto/pbkdf2"
20
 	"golang.org/x/crypto/pbkdf2"
19
 )
21
 )
20
 
22
 
24
 
26
 
25
 	hmacServerKeyText    = []byte("Server Key")
27
 	hmacServerKeyText    = []byte("Server Key")
26
 	athemePBKDF2V2Prefix = []byte("$z")
28
 	athemePBKDF2V2Prefix = []byte("$z")
29
+	athemeRawSHA1Prefix  = []byte("$rawsha1$")
27
 )
30
 )
28
 
31
 
29
 type PassphraseCheck func(hash, passphrase []byte) (err error)
32
 type PassphraseCheck func(hash, passphrase []byte) (err error)
30
 
33
 
31
 func CheckAthemePassphrase(hash, passphrase []byte) (err error) {
34
 func CheckAthemePassphrase(hash, passphrase []byte) (err error) {
32
-	if len(hash) < 60 {
33
-		return checkAthemePosixCrypt(hash, passphrase)
35
+	if bytes.HasPrefix(hash, athemeRawSHA1Prefix) {
36
+		return checkAthemeRawSha1(hash, passphrase)
34
 	} else if bytes.HasPrefix(hash, athemePBKDF2V2Prefix) {
37
 	} else if bytes.HasPrefix(hash, athemePBKDF2V2Prefix) {
35
 		return checkAthemePBKDF2V2(hash, passphrase)
38
 		return checkAthemePBKDF2V2(hash, passphrase)
39
+	} else if len(hash) < 60 {
40
+		return checkAthemePosixCrypt(hash, passphrase)
36
 	} else {
41
 	} else {
37
 		return checkAthemePBKDF2(hash, passphrase)
42
 		return checkAthemePBKDF2(hash, passphrase)
38
 	}
43
 	}
181
 		return ErrHashCheckFailed
186
 		return ErrHashCheckFailed
182
 	}
187
 	}
183
 }
188
 }
189
+
190
+func checkAthemeRawSha1(hash, passphrase []byte) (err error) {
191
+	return checkRawHash(hash[len(athemeRawSHA1Prefix):], passphrase, sha1.New())
192
+}
193
+
194
+func checkRawHash(expected, passphrase []byte, h hash.Hash) (err error) {
195
+	var rawExpected []byte
196
+	size := h.Size()
197
+	if len(expected) == 2*size {
198
+		rawExpected = make([]byte, h.Size())
199
+		_, err = hex.Decode(rawExpected, expected)
200
+		if err != nil {
201
+			return ErrHashInvalid
202
+		}
203
+	} else if len(expected) == size {
204
+		rawExpected = expected
205
+	} else {
206
+		return ErrHashInvalid
207
+	}
208
+
209
+	h.Write(passphrase)
210
+	hashedPassphrase := h.Sum(nil)
211
+	if subtle.ConstantTimeCompare(rawExpected, hashedPassphrase) == 1 {
212
+		return nil
213
+	} else {
214
+		return ErrHashCheckFailed
215
+	}
216
+}
217
+
218
+func checkAnopeEncSha256(hashBytes, ivBytes, passphrase []byte) (err error) {
219
+	if len(ivBytes) != 32 {
220
+		return ErrHashInvalid
221
+	}
222
+	// https://github.com/anope/anope/blob/2cf507ed662620d0b97c8484fbfbfa09265e86e1/modules/encryption/enc_sha256.cpp#L67
223
+	var iv [8]uint32
224
+	for i := 0; i < 8; i++ {
225
+		iv[i] = binary.BigEndian.Uint32(ivBytes[i*4 : (i+1)*4])
226
+	}
227
+	result := anopeSum256(passphrase, iv)
228
+	if subtle.ConstantTimeCompare(result[:], hashBytes) == 1 {
229
+		return nil
230
+	} else {
231
+		return ErrHashCheckFailed
232
+	}
233
+}
234
+
235
+func CheckAnopePassphrase(hash, passphrase []byte) (err error) {
236
+	pieces := bytes.Split(hash, []byte{':'})
237
+	if len(pieces) < 2 {
238
+		return ErrHashInvalid
239
+	}
240
+	switch string(pieces[0]) {
241
+	case "plain":
242
+		// base64, standard encoding
243
+		expectedPassphrase, err := base64.StdEncoding.DecodeString(string(pieces[1]))
244
+		if err != nil {
245
+			return ErrHashInvalid
246
+		}
247
+		if subtle.ConstantTimeCompare(passphrase, expectedPassphrase) == 1 {
248
+			return nil
249
+		} else {
250
+			return ErrHashCheckFailed
251
+		}
252
+	case "md5":
253
+		// raw MD5
254
+		return checkRawHash(pieces[1], passphrase, md5.New())
255
+	case "sha1":
256
+		// raw SHA-1
257
+		return checkRawHash(pieces[1], passphrase, sha1.New())
258
+	case "bcrypt":
259
+		if bcrypt.CompareHashAndPassword(pieces[1], passphrase) == nil {
260
+			return nil
261
+		} else {
262
+			return ErrHashCheckFailed
263
+		}
264
+	case "sha256":
265
+		// SHA-256 with an overridden IV
266
+		if len(pieces) != 3 {
267
+			return ErrHashInvalid
268
+		}
269
+		hashBytes, err := hex.DecodeString(string(pieces[1]))
270
+		if err != nil {
271
+			return ErrHashInvalid
272
+		}
273
+		ivBytes, err := hex.DecodeString(string(pieces[2]))
274
+		if err != nil {
275
+			return ErrHashInvalid
276
+		}
277
+		return checkAnopeEncSha256(hashBytes, ivBytes, passphrase)
278
+	default:
279
+		return ErrHashInvalid
280
+	}
281
+}

+ 142
- 0
irc/migrations/passwords_test.go 查看文件

11
 func TestAthemePassphrases(t *testing.T) {
11
 func TestAthemePassphrases(t *testing.T) {
12
 	var err error
12
 	var err error
13
 
13
 
14
+	// modules/crypto/crypt3-md5:
14
 	err = CheckAthemePassphrase([]byte("$1$hcspif$nCm4r3S14Me9ifsOPGuJT."), []byte("shivarampassphrase"))
15
 	err = CheckAthemePassphrase([]byte("$1$hcspif$nCm4r3S14Me9ifsOPGuJT."), []byte("shivarampassphrase"))
15
 	if err != nil {
16
 	if err != nil {
16
 		t.Errorf("failed to check passphrase: %v", err)
17
 		t.Errorf("failed to check passphrase: %v", err)
21
 		t.Errorf("accepted invalid passphrase")
22
 		t.Errorf("accepted invalid passphrase")
22
 	}
23
 	}
23
 
24
 
25
+	err = CheckAthemePassphrase([]byte("$1$diwesm$9MjapdOyhyC.2FdHzKMzK."), []byte("1Ss1GN4q-3e8SgIJblfQxw"))
26
+	if err != nil {
27
+		t.Errorf("failed to check passphrase: %v", err)
28
+	}
29
+	err = CheckAthemePassphrase([]byte("$1$hcspif$nCm4r3S14Me9ifsOPGuJT."), []byte("sh1varampassphrase"))
30
+	if err == nil {
31
+		t.Errorf("accepted invalid passphrase")
32
+	}
33
+
34
+	// modules/crypto/pbkdf2:
24
 	err = CheckAthemePassphrase([]byte("khMlbBBIFya2ihyN42abc3e768663e2c4fd0e0020e46292bf9fdf44e9a51d2a2e69509cb73b4b1bf9c1b6355a1fc9ea663fcd6da902287159494f15b905e5e651d6a60f2ec834598"), []byte("password"))
35
 	err = CheckAthemePassphrase([]byte("khMlbBBIFya2ihyN42abc3e768663e2c4fd0e0020e46292bf9fdf44e9a51d2a2e69509cb73b4b1bf9c1b6355a1fc9ea663fcd6da902287159494f15b905e5e651d6a60f2ec834598"), []byte("password"))
25
 	if err != nil {
36
 	if err != nil {
26
 		t.Errorf("failed to check passphrase: %v", err)
37
 		t.Errorf("failed to check passphrase: %v", err)
31
 		t.Errorf("accepted invalid passphrase")
42
 		t.Errorf("accepted invalid passphrase")
32
 	}
43
 	}
33
 
44
 
45
+	// modules/crypto/pbkdf2v2:
34
 	err = CheckAthemePassphrase([]byte("$z$65$64000$1kz1I9YJPJ2gkJALbrpL2DoxRDhYPBOg60KNJMK/6do=$Cnfg6pYhBNrVXiaXYH46byrC+3HKet/XvYwvI1BvZbs=$m0hrT33gcF90n2TU3lm8tdm9V9XC4xEV13KsjuT38iY="), []byte("password"))
46
 	err = CheckAthemePassphrase([]byte("$z$65$64000$1kz1I9YJPJ2gkJALbrpL2DoxRDhYPBOg60KNJMK/6do=$Cnfg6pYhBNrVXiaXYH46byrC+3HKet/XvYwvI1BvZbs=$m0hrT33gcF90n2TU3lm8tdm9V9XC4xEV13KsjuT38iY="), []byte("password"))
35
 	if err != nil {
47
 	if err != nil {
36
 		t.Errorf("failed to check passphrase: %v", err)
48
 		t.Errorf("failed to check passphrase: %v", err)
40
 	if err == nil {
52
 	if err == nil {
41
 		t.Errorf("accepted invalid passphrase")
53
 		t.Errorf("accepted invalid passphrase")
42
 	}
54
 	}
55
+
56
+	weirdHash := []byte("$z$6$64000$rWfIGzPY9qiIt7m5$VdFroDOlTQSLlFUJtpvlbp2i7sH3ZUndqwdnOvoDvt6b2AzLjaAK/lhSO/QaR2nA3Wm4ObHdl3WMW32NdtSMdw==")
57
+	err = CheckAthemePassphrase(weirdHash, []byte("pHQpwje5CjS3_Lx0RaeS7w"))
58
+	if err != nil {
59
+		t.Errorf("failed to check passphrase: %v", err)
60
+	}
61
+	err = CheckAthemePassphrase(weirdHash, []byte("pHQpwje5CjS3-Lx0RaeS7w"))
62
+	if err == nil {
63
+		t.Errorf("accepted invalid passphrase")
64
+	}
65
+}
66
+
67
+func TestAthemeRawSha1(t *testing.T) {
68
+	var err error
69
+
70
+	shivaramHash := []byte("$rawsha1$49fffa5543f21dd6effe88a79633e4073e36a828")
71
+	err = CheckAthemePassphrase(shivaramHash, []byte("shivarampassphrase"))
72
+	if err != nil {
73
+		t.Errorf("failed to check passphrase: %v", err)
74
+	}
75
+	err = CheckAthemePassphrase(shivaramHash, []byte("edpassphrase"))
76
+	if err == nil {
77
+		t.Errorf("accepted invalid passphrase")
78
+	}
43
 }
79
 }
44
 
80
 
45
 func TestOragonoLegacyPassphrase(t *testing.T) {
81
 func TestOragonoLegacyPassphrase(t *testing.T) {
70
 		t.Errorf("accepted invalid passphrase")
106
 		t.Errorf("accepted invalid passphrase")
71
 	}
107
 	}
72
 }
108
 }
109
+
110
+func TestAnopePassphraseRawSha1(t *testing.T) {
111
+	var err error
112
+	shivaramHash := []byte("sha1:49fffa5543f21dd6effe88a79633e4073e36a828")
113
+	err = CheckAnopePassphrase(shivaramHash, []byte("shivarampassphrase"))
114
+	if err != nil {
115
+		t.Errorf("failed to check passphrase: %v", err)
116
+	}
117
+	err = CheckAnopePassphrase(shivaramHash, []byte("edpassphrase"))
118
+	if err == nil {
119
+		t.Errorf("accepted invalid passphrase")
120
+	}
121
+
122
+	edHash := []byte("sha1:ea44e256819de972c25fef0aa277396067d6024f")
123
+	err = CheckAnopePassphrase(edHash, []byte("edpassphrase"))
124
+	if err != nil {
125
+		t.Errorf("failed to check passphrase: %v", err)
126
+	}
127
+	err = CheckAnopePassphrase(edHash, []byte("shivarampassphrase"))
128
+	if err == nil {
129
+		t.Errorf("accepted invalid passphrase")
130
+	}
131
+}
132
+
133
+func TestAnopePassphraseRawMd5(t *testing.T) {
134
+	var err error
135
+	shivaramHash := []byte("md5:ce4bd864f37ffaa1b871aef22eea82ff")
136
+	err = CheckAnopePassphrase(shivaramHash, []byte("shivarampassphrase"))
137
+	if err != nil {
138
+		t.Errorf("failed to check passphrase: %v", err)
139
+	}
140
+	err = CheckAnopePassphrase(shivaramHash, []byte("edpassphrase"))
141
+	if err == nil {
142
+		t.Errorf("accepted invalid passphrase")
143
+	}
144
+
145
+	edHash := []byte("md5:dbf8be80e8dccdd33915b482e4390426")
146
+	err = CheckAnopePassphrase(edHash, []byte("edpassphrase"))
147
+	if err != nil {
148
+		t.Errorf("failed to check passphrase: %v", err)
149
+	}
150
+	err = CheckAnopePassphrase(edHash, []byte("shivarampassphrase"))
151
+	if err == nil {
152
+		t.Errorf("accepted invalid passphrase")
153
+	}
154
+}
155
+
156
+func TestAnopePassphrasePlain(t *testing.T) {
157
+	var err error
158
+	// not actually a hash
159
+	weirdHash := []byte("plain:YVxzMC1fMmZ+ZjM0OEAhN2FzZGYxNDJAIyFhZmE=")
160
+	err = CheckAnopePassphrase(weirdHash, []byte("a\\s0-_2f~f348@!7asdf142@#!afa"))
161
+	if err != nil {
162
+		t.Errorf("failed to check passphrase: %v", err)
163
+	}
164
+	err = CheckAnopePassphrase(weirdHash, []byte("edpassphrase"))
165
+	if err == nil {
166
+		t.Errorf("accepted invalid passphrase")
167
+	}
168
+}
169
+
170
+func TestAnopePassphraseBcrypt(t *testing.T) {
171
+	var err error
172
+	shivaramHash := []byte("bcrypt:$2a$10$UyNgHyniPukGf/3A6vzBx.VMNfej0h4WzATg4ahKW2H86a0QLcVIK")
173
+	err = CheckAnopePassphrase(shivaramHash, []byte("shivarampassphrase"))
174
+	if err != nil {
175
+		t.Errorf("failed to check passphrase: %v", err)
176
+	}
177
+	err = CheckAnopePassphrase(shivaramHash, []byte("edpassphrase"))
178
+	if err == nil {
179
+		t.Errorf("accepted invalid passphrase")
180
+	}
181
+}
182
+
183
+func TestAnopePassphraseEncSha256(t *testing.T) {
184
+	var err error
185
+	shivaramHash := []byte("sha256:ff337943c8c4219cd330a3075a699492e0f8b1a823bb76af0129f1f117ba0630:60250c3053f7b34e35576fc5063b8b396fe7b9ab416842117991a8e027aa72f6")
186
+	err = CheckAnopePassphrase(shivaramHash, []byte("shivarampassphrase"))
187
+	if err != nil {
188
+		t.Errorf("failed to check passphrase: %v", err)
189
+	}
190
+	err = CheckAnopePassphrase(shivaramHash, []byte("edpassphrase"))
191
+	if err == nil {
192
+		t.Errorf("accepted invalid passphrase")
193
+	}
194
+
195
+	edHash := []byte("sha256:93a430c8c3c6917dc6e9a32ac1aba90bc5768265278a45b86eacd636fc723d8f:10ea72683a499c155d72cd3571cb80e5050280620f789a44492c0e0c7956942f")
196
+	err = CheckAnopePassphrase(edHash, []byte("edpassphrase"))
197
+	if err != nil {
198
+		t.Errorf("failed to check passphrase: %v", err)
199
+	}
200
+	err = CheckAnopePassphrase(edHash, []byte("shivarampassphrase"))
201
+	if err == nil {
202
+		t.Errorf("accepted invalid passphrase")
203
+	}
204
+
205
+	weirdHash := []byte("sha256:06d11a06025354e37a7ddf48913a1c9831ffab47d04e4c22a89fd7835abcb6cc:3137788c2749da0419bc9df320991d2d72495c7065da4f39004fd21710601409")
206
+	err = CheckAnopePassphrase(weirdHash, []byte("1Ss1GN4q-3e8SgIJblfQxw"))
207
+	if err != nil {
208
+		t.Errorf("failed to check passphrase: %v", err)
209
+	}
210
+	err = CheckAnopePassphrase(weirdHash, []byte("shivarampassphrase"))
211
+	if err == nil {
212
+		t.Errorf("accepted invalid passphrase")
213
+	}
214
+}

+ 128
- 0
irc/migrations/sha256.go 查看文件

1
+/*
2
+Copyright (c) 2009 The Go Authors. All rights reserved.
3
+
4
+Redistribution and use in source and binary forms, with or without
5
+modification, are permitted provided that the following conditions are
6
+met:
7
+
8
+   * Redistributions of source code must retain the above copyright
9
+notice, this list of conditions and the following disclaimer.
10
+   * Redistributions in binary form must reproduce the above
11
+copyright notice, this list of conditions and the following disclaimer
12
+in the documentation and/or other materials provided with the
13
+distribution.
14
+   * Neither the name of Google Inc. nor the names of its
15
+contributors may be used to endorse or promote products derived from
16
+this software without specific prior written permission.
17
+
18
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+*/
30
+
31
+// SHA256 implementation from golang/go, modified to accommodate anope's
32
+// password hashing scheme, which overrides the initialization vector
33
+// using the salt.
34
+
35
+package migrations
36
+
37
+import (
38
+	"encoding/binary"
39
+)
40
+
41
+// The size of a SHA256 checksum in bytes.
42
+const Size = 32
43
+
44
+const (
45
+	chunk = 64
46
+)
47
+
48
+// digest represents the partial evaluation of a checksum.
49
+type digest struct {
50
+	h   [8]uint32
51
+	x   [chunk]byte
52
+	nx  int
53
+	len uint64
54
+}
55
+
56
+func (d *digest) Write(p []byte) (nn int, err error) {
57
+	nn = len(p)
58
+	d.len += uint64(nn)
59
+	if d.nx > 0 {
60
+		n := copy(d.x[d.nx:], p)
61
+		d.nx += n
62
+		if d.nx == chunk {
63
+			sha256BlockGeneric(d, d.x[:])
64
+			d.nx = 0
65
+		}
66
+		p = p[n:]
67
+	}
68
+	if len(p) >= chunk {
69
+		n := len(p) &^ (chunk - 1)
70
+		sha256BlockGeneric(d, p[:n])
71
+		p = p[n:]
72
+	}
73
+	if len(p) > 0 {
74
+		d.nx = copy(d.x[:], p)
75
+	}
76
+	return
77
+}
78
+
79
+func (d *digest) Sum(in []byte) []byte {
80
+	// Make a copy of d so that caller can keep writing and summing.
81
+	d0 := *d
82
+	hash := d0.checkSum()
83
+	return append(in, hash[:]...)
84
+}
85
+
86
+func (d *digest) checkSum() [Size]byte {
87
+	len := d.len
88
+	// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
89
+	var tmp [64]byte
90
+	tmp[0] = 0x80
91
+	if len%64 < 56 {
92
+		d.Write(tmp[0 : 56-len%64])
93
+	} else {
94
+		d.Write(tmp[0 : 64+56-len%64])
95
+	}
96
+
97
+	// Length in bits.
98
+	len <<= 3
99
+	binary.BigEndian.PutUint64(tmp[:], len)
100
+	d.Write(tmp[0:8])
101
+
102
+	if d.nx != 0 {
103
+		panic("d.nx != 0")
104
+	}
105
+
106
+	var digest [Size]byte
107
+
108
+	binary.BigEndian.PutUint32(digest[0:], d.h[0])
109
+	binary.BigEndian.PutUint32(digest[4:], d.h[1])
110
+	binary.BigEndian.PutUint32(digest[8:], d.h[2])
111
+	binary.BigEndian.PutUint32(digest[12:], d.h[3])
112
+	binary.BigEndian.PutUint32(digest[16:], d.h[4])
113
+	binary.BigEndian.PutUint32(digest[20:], d.h[5])
114
+	binary.BigEndian.PutUint32(digest[24:], d.h[6])
115
+	binary.BigEndian.PutUint32(digest[28:], d.h[7])
116
+
117
+	return digest
118
+}
119
+
120
+// Anope password hashing function: SHA-256 with an override for the IV
121
+// The actual SHA-256 IV for reference:
122
+// [8]uint32{0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19}
123
+func anopeSum256(data []byte, iv [8]uint32) [Size]byte {
124
+	var d digest
125
+	d.h = iv
126
+	d.Write(data)
127
+	return d.checkSum()
128
+}

+ 154
- 0
irc/migrations/sha256block.go 查看文件

1
+/*
2
+Copyright (c) 2009 The Go Authors. All rights reserved.
3
+
4
+Redistribution and use in source and binary forms, with or without
5
+modification, are permitted provided that the following conditions are
6
+met:
7
+
8
+   * Redistributions of source code must retain the above copyright
9
+notice, this list of conditions and the following disclaimer.
10
+   * Redistributions in binary form must reproduce the above
11
+copyright notice, this list of conditions and the following disclaimer
12
+in the documentation and/or other materials provided with the
13
+distribution.
14
+   * Neither the name of Google Inc. nor the names of its
15
+contributors may be used to endorse or promote products derived from
16
+this software without specific prior written permission.
17
+
18
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+*/
30
+
31
+// SHA256 block step.
32
+// In its own file so that a faster assembly or C version
33
+// can be substituted easily.
34
+
35
+package migrations
36
+
37
+import "math/bits"
38
+
39
+var _K = []uint32{
40
+	0x428a2f98,
41
+	0x71374491,
42
+	0xb5c0fbcf,
43
+	0xe9b5dba5,
44
+	0x3956c25b,
45
+	0x59f111f1,
46
+	0x923f82a4,
47
+	0xab1c5ed5,
48
+	0xd807aa98,
49
+	0x12835b01,
50
+	0x243185be,
51
+	0x550c7dc3,
52
+	0x72be5d74,
53
+	0x80deb1fe,
54
+	0x9bdc06a7,
55
+	0xc19bf174,
56
+	0xe49b69c1,
57
+	0xefbe4786,
58
+	0x0fc19dc6,
59
+	0x240ca1cc,
60
+	0x2de92c6f,
61
+	0x4a7484aa,
62
+	0x5cb0a9dc,
63
+	0x76f988da,
64
+	0x983e5152,
65
+	0xa831c66d,
66
+	0xb00327c8,
67
+	0xbf597fc7,
68
+	0xc6e00bf3,
69
+	0xd5a79147,
70
+	0x06ca6351,
71
+	0x14292967,
72
+	0x27b70a85,
73
+	0x2e1b2138,
74
+	0x4d2c6dfc,
75
+	0x53380d13,
76
+	0x650a7354,
77
+	0x766a0abb,
78
+	0x81c2c92e,
79
+	0x92722c85,
80
+	0xa2bfe8a1,
81
+	0xa81a664b,
82
+	0xc24b8b70,
83
+	0xc76c51a3,
84
+	0xd192e819,
85
+	0xd6990624,
86
+	0xf40e3585,
87
+	0x106aa070,
88
+	0x19a4c116,
89
+	0x1e376c08,
90
+	0x2748774c,
91
+	0x34b0bcb5,
92
+	0x391c0cb3,
93
+	0x4ed8aa4a,
94
+	0x5b9cca4f,
95
+	0x682e6ff3,
96
+	0x748f82ee,
97
+	0x78a5636f,
98
+	0x84c87814,
99
+	0x8cc70208,
100
+	0x90befffa,
101
+	0xa4506ceb,
102
+	0xbef9a3f7,
103
+	0xc67178f2,
104
+}
105
+
106
+func sha256BlockGeneric(dig *digest, p []byte) {
107
+	var w [64]uint32
108
+	h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7]
109
+	for len(p) >= chunk {
110
+		// Can interlace the computation of w with the
111
+		// rounds below if needed for speed.
112
+		for i := 0; i < 16; i++ {
113
+			j := i * 4
114
+			w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3])
115
+		}
116
+		for i := 16; i < 64; i++ {
117
+			v1 := w[i-2]
118
+			t1 := (bits.RotateLeft32(v1, -17)) ^ (bits.RotateLeft32(v1, -19)) ^ (v1 >> 10)
119
+			v2 := w[i-15]
120
+			t2 := (bits.RotateLeft32(v2, -7)) ^ (bits.RotateLeft32(v2, -18)) ^ (v2 >> 3)
121
+			w[i] = t1 + w[i-7] + t2 + w[i-16]
122
+		}
123
+
124
+		a, b, c, d, e, f, g, h := h0, h1, h2, h3, h4, h5, h6, h7
125
+
126
+		for i := 0; i < 64; i++ {
127
+			t1 := h + ((bits.RotateLeft32(e, -6)) ^ (bits.RotateLeft32(e, -11)) ^ (bits.RotateLeft32(e, -25))) + ((e & f) ^ (^e & g)) + _K[i] + w[i]
128
+
129
+			t2 := ((bits.RotateLeft32(a, -2)) ^ (bits.RotateLeft32(a, -13)) ^ (bits.RotateLeft32(a, -22))) + ((a & b) ^ (a & c) ^ (b & c))
130
+
131
+			h = g
132
+			g = f
133
+			f = e
134
+			e = d + t1
135
+			d = c
136
+			c = b
137
+			b = a
138
+			a = t1 + t2
139
+		}
140
+
141
+		h0 += a
142
+		h1 += b
143
+		h2 += c
144
+		h3 += d
145
+		h4 += e
146
+		h5 += f
147
+		h6 += g
148
+		h7 += h
149
+
150
+		p = p[chunk:]
151
+	}
152
+
153
+	dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] = h0, h1, h2, h3, h4, h5, h6, h7
154
+}

+ 4
- 4
irc/modes/modes.go 查看文件

89
 type Modes []Mode
89
 type Modes []Mode
90
 
90
 
91
 func (modes Modes) String() string {
91
 func (modes Modes) String() string {
92
-	strs := make([]string, len(modes))
93
-	for index, mode := range modes {
94
-		strs[index] = mode.String()
92
+	var builder strings.Builder
93
+	for _, m := range modes {
94
+		builder.WriteRune(rune(m))
95
 	}
95
 	}
96
-	return strings.Join(strs, "")
96
+	return builder.String()
97
 }
97
 }
98
 
98
 
99
 // User Modes
99
 // User Modes

Loading…
取消
儲存