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() error {
87 if c := udp.credentials; c != nil {
88 defer runtime.GC() // any better way to clean the plaintexts?
96 return errors.New("No credentials stored")
99 // Saves the used credentials in the AniDB struct, to allow automatic
100 // re-authentication when needed; they are (properly) encrypted with a key that's
101 // uniquely generated every time the module is initialized.
102 func (adb *AniDB) SetCredentials(username, password, udpKey string) {
103 adb.udp.credentials.shred()
104 adb.udp.credentials = newCredentials(username, password, udpKey)
107 // Authenticates to anidb's UDP API and, on success, stores the credentials using
108 // SetCredentials. If udpKey is not "", the communication with the server
109 // will be encrypted, but in the VERY weak ECB mode.
110 func (adb *AniDB) Auth(username, password, udpKey string) (err error) {
111 defer runtime.GC() // any better way to clean the plaintexts?
113 if err = adb.udp.Auth(username, password, udpKey); err == nil {
114 adb.udp.connected = true
115 adb.SetCredentials(username, password, udpKey)
120 // Logs the user out and removes the credentials from the AniDB struct.
121 func (adb *AniDB) Logout() error {
122 adb.udp.credentials.shred()
123 adb.udp.credentials = nil
125 if adb.udp.connected {
126 adb.udp.connected = false
127 return adb.udp.Logout()