decode(String)   B
last analyzed

Complexity

Conditions 6

Size

Total Lines 42
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 22
c 1
b 0
f 0
dl 0
loc 42
ccs 19
cts 19
cp 1
crap 6
rs 8.4186
1
/*
2
 * This file is part of ArakneUtils.
3
 *
4
 * ArakneUtils is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU Lesser General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * ArakneUtils is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public License
15
 * along with ArakneUtils.  If not, see <https://www.gnu.org/licenses/>.
16
 *
17
 * Copyright (c) 2017-2020 Vincent Quatrevieux
18
 */
19
20
package fr.arakne.utils.encoding;
21
22
import org.checkerframework.checker.index.qual.LTLengthOf;
23
import org.checkerframework.dataflow.qual.Pure;
24
import org.checkerframework.dataflow.qual.SideEffectFree;
25
26
/**
27
 * The password encoding algorithm for Dofus
28
 * The algo is a pseudo base 64 vigenere cypher implementation
29
 *
30
 * Note: This is not a safe method for store or communicate password.
31
 *       You must use this algo only for compatibility with Dofus 1.
32
 *
33
 * Usage:
34
 * <code>
35
 *     PasswordEncoder encoder = new PasswordEncoder("my connection key");
36
 *
37
 *     encoder.encode("my password"); // Encode password for send to the server
38
 *     encoder.decode(encodedPassword); // Decode the password
39
 * </code>
40
 *
41
 * https://github.com/Emudofus/Dofus/blob/1.29/ank/utils/Crypt.as#L20
42
 */
43
public final class PasswordEncoder {
44
    private final String key;
45
46
    /**
47
     * @param key The key to use. It must be long enough to encode the password.
48
     */
49 1
    public PasswordEncoder(String key) {
50 1
        this.key = key;
51 1
    }
52
53
    /**
54
     * Get the encoding key
55
     *
56
     * @return The key
57
     */
58
    @Pure
59
    public String key() {
60 1
        return key;
61
    }
62
63
    /**
64
     * Decode given string using key
65
     *
66
     * @param encoded Encoded string
67
     *
68
     * @return Decoded string, in Dofus base 64 format
69
     *
70
     * @throws IllegalArgumentException When the encoded string is invalid, or the key is too small
71
     */
72
    @SideEffectFree
73
    @SuppressWarnings("assignment") // i / 2 is not resolved as key and decoded length
74
    public String decode(String encoded) {
75 1
        if (encoded.length() % 2 != 0) {
76 1
            throw new IllegalArgumentException("Invalid encoded string");
77
        }
78
79 1
        if (key.length() * 2 < encoded.length()) {
80 1
            throw new IllegalArgumentException("Encoded string is too long for the key");
81
        }
82
83 1
        final char[] decoded = new char[encoded.length() / 2];
84
85
        // Iterate over pair chars
86 1
        for (int i = 0; i < encoded.length() - 1; i += 2) {
87 1
            final @LTLengthOf({"key", "decoded"}) int p = i / 2;
88 1
            final int k = key.charAt(p) % 64; // Get key char
89
90
            // Get two chars int value (divider and modulo)
91 1
            int d = Base64.ord(encoded.charAt(i));
92 1
            int r = Base64.ord(encoded.charAt(i + 1));
93
94
            // Remove key value
95 1
            d -= k;
96 1
            r -= k;
97
98
            // if values are negative (due to modulo), reverse the module
99 1
            while (d < 0) {
100 1
                d += 64;
101
            }
102
103 1
            while (r < 0) {
104 1
                r += 64;
105
            }
106
107
            // retrieve the original value
108 1
            final int v = d * 16 + r;
109
110 1
            decoded[p] = (char) v;
111
        }
112
113 1
        return new String(decoded);
114
    }
115
116
    /**
117
     * Encode the password using the key
118
     *
119
     * @param password The raw password
120
     * @return The encoded password
121
     *
122
     * @throws IllegalArgumentException When the password is too long
123
     */
124
    @SideEffectFree
125
    @SuppressWarnings({"argument", "array.access.unsafe.high"}) // Length are not resolved
126
    public String encode(String password) {
127 1
        if (key.length() < password.length()) {
128 1
            throw new IllegalArgumentException("The password is too long for the key");
129
        }
130
131 1
        final char[] encoded = new char[password.length() * 2];
132
133 1
        for (int i = 0; i < password.length(); ++i) {
134
            // Password char and key
135 1
            final char c = password.charAt(i);
136 1
            final char k = key.charAt(i);
137
138
            // Get the divider and modulo values
139 1
            final int d = c / 16;
140 1
            final int r = c % 16;
141
142
            // Encode into base64
143 1
            encoded[i * 2] = Base64.chrMod(d + k);
144 1
            encoded[i * 2 + 1] = Base64.chrMod(r + k);
145
        }
146
147 1
        return new String(encoded);
148
    }
149
}
150