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 // Re-authenticates the current user. Do not use unless really necessary.
87 func (a *AniDB) ReAuth() error {
91 func (udp *udpWrap) ReAuth() error {
92 if c := udp.credentials; c != nil {
93 defer runtime.GC() // any better way to clean the plaintexts?
101 return errors.New("No credentials stored")
104 // Saves the used credentials in the AniDB struct, to allow automatic
105 // re-authentication when needed; they are (properly) encrypted with a key that's
106 // uniquely generated every time the module is initialized.
107 func (a *AniDB) SetCredentials(username, password, udpKey string) {
108 a.udp.credentials.shred()
109 a.udp.credentials = newCredentials(username, password, udpKey)
112 // Authenticates to anidb's UDP API and, on success, stores the credentials using
113 // SetCredentials. If udpKey is not "", the communication with the server
114 // will be encrypted, but in the VERY weak ECB mode.
115 func (a *AniDB) Auth(username, password, udpKey string) (err error) {
116 defer runtime.GC() // any better way to clean the plaintexts?
118 if err = a.udp.Auth(username, password, udpKey); err == nil {
119 a.udp.connected = true
120 a.SetCredentials(username, password, udpKey)
125 // Logs the user out and removes the credentials from the AniDB struct.
126 func (a *AniDB) Logout() error {
128 a.udp.credentials.shred()
129 a.udp.credentials = nil
131 a.udp.connected = false
132 return a.udp.Logout()