7 "github.com/Kovensky/go-anidb/udp"
12 // We still have the key and IV somewhere in memory...
13 // but it's better than plaintext.
14 type credentials struct {
20 func (c *credentials) shred() {
22 io.ReadFull(rand.Reader, c.username)
23 io.ReadFull(rand.Reader, c.password)
24 io.ReadFull(rand.Reader, c.udpKey)
31 // Randomly generated on every execution
35 aesKey = make([]byte, aes.BlockSize)
36 if _, err := io.ReadFull(rand.Reader, aesKey); err != nil {
41 func crypt(plaintext string) []byte {
42 p := []byte(plaintext)
44 block, err := aes.NewCipher(aesKey)
49 ciphertext := make([]byte, len(p)+aes.BlockSize)
50 iv := ciphertext[:aes.BlockSize]
51 if _, err := io.ReadFull(rand.Reader, iv); err != nil {
55 stream := cipher.NewCTR(block, iv)
56 stream.XORKeyStream(ciphertext[aes.BlockSize:], p)
61 func decrypt(ciphertext []byte) string {
62 if len(ciphertext) <= aes.BlockSize {
65 p := make([]byte, len(ciphertext)-aes.BlockSize)
67 block, err := aes.NewCipher(aesKey)
72 stream := cipher.NewCTR(block, ciphertext[:aes.BlockSize])
73 stream.XORKeyStream(p, ciphertext[aes.BlockSize:])
78 func newCredentials(username, password, udpKey string) *credentials {
80 username: crypt(username),
81 password: crypt(password),
82 udpKey: crypt(udpKey),
86 func (udp *udpWrap) ReAuth() udpapi.APIReply {
92 defer udp.credLock.Unlock()
94 if c := udp.credentials; c != nil {
95 logRequest(paramSet{cmd: "AUTH", params: paramMap{"user": decrypt(c.username)}})
96 r := udp.AniDBUDP.Auth(
100 runtime.GC() // any better way to clean the plaintexts?
108 // 601 -- server down, treat the same as a ban
111 case 500: // bad credentials
112 udp.credentials.shred()
113 udp.credentials = nil
114 case 503, 504: // client rejected
118 udp.connected = err == nil
121 return &noauthAPIReply{}
124 // Saves the used credentials in the AniDB struct, to allow automatic
125 // re-authentication when needed; they are (properly) encrypted with a key that's
126 // uniquely generated every time the module is initialized.
127 func (adb *AniDB) SetCredentials(username, password, udpKey string) {
128 adb.udp.credLock.Lock()
129 defer adb.udp.credLock.Unlock()
131 adb.udp.credentials.shred()
132 adb.udp.credentials = newCredentials(username, password, udpKey)
135 // Authenticates to anidb's UDP API and, on success, stores the credentials using
136 // SetCredentials. If udpKey is not "", the communication with the server
137 // will be encrypted, but in the VERY weak ECB mode.
138 func (adb *AniDB) Auth(username, password, udpKey string) (err error) {
139 defer runtime.GC() // any better way to clean the plaintexts?
141 adb.udp.sendLock.Lock()
142 defer adb.udp.sendLock.Unlock()
145 adb.SetCredentials(username, password, udpKey)
148 // ReAuth clears the credentials if they're bad
149 return adb.udp.ReAuth().Error()
152 // Logs the user out and removes the credentials from the AniDB struct.
153 func (adb *AniDB) Logout() error {
154 adb.udp.credLock.Lock()
155 defer adb.udp.credLock.Unlock()
157 adb.udp.credentials.shred()
158 adb.udp.credentials = nil
160 adb.udp.sendLock.Lock()
161 defer adb.udp.sendLock.Unlock()
163 if adb.udp.connected {
164 adb.udp.connected = false
165 logRequest(paramSet{cmd: "LOGOUT"})
166 return adb.udp.Logout()