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 r := udp.AniDBUDP.Auth(
99 runtime.GC() // any better way to clean the plaintexts?
106 // 601 -- server down, treat the same as a ban
109 case 500: // bad credentials
110 udp.credentials.shred()
111 udp.credentials = nil
112 case 503, 504: // client rejected
116 udp.connected = err == nil
119 return &noauthAPIReply{}
122 // Saves the used credentials in the AniDB struct, to allow automatic
123 // re-authentication when needed; they are (properly) encrypted with a key that's
124 // uniquely generated every time the module is initialized.
125 func (adb *AniDB) SetCredentials(username, password, udpKey string) {
126 adb.udp.credLock.Lock()
127 defer adb.udp.credLock.Unlock()
129 adb.udp.credentials.shred()
130 adb.udp.credentials = newCredentials(username, password, udpKey)
133 // Authenticates to anidb's UDP API and, on success, stores the credentials using
134 // SetCredentials. If udpKey is not "", the communication with the server
135 // will be encrypted, but in the VERY weak ECB mode.
136 func (adb *AniDB) Auth(username, password, udpKey string) (err error) {
137 defer runtime.GC() // any better way to clean the plaintexts?
139 adb.udp.sendLock.Lock()
140 defer adb.udp.sendLock.Unlock()
143 adb.SetCredentials(username, password, udpKey)
146 // ReAuth clears the credentials if they're bad
147 return adb.udp.ReAuth().Error()
150 // Logs the user out and removes the credentials from the AniDB struct.
151 func (adb *AniDB) Logout() error {
152 adb.udp.credLock.Lock()
153 defer adb.udp.credLock.Unlock()
155 adb.udp.credentials.shred()
156 adb.udp.credentials = nil
158 adb.udp.sendLock.Lock()
159 defer adb.udp.sendLock.Unlock()
161 if adb.udp.connected {
162 adb.udp.connected = false
163 return adb.udp.Logout()