fr.arakne.utils.encoding.Key.parse(String)   A
last analyzed

Complexity

Conditions 4

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 4.25

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 12
c 1
b 0
f 0
dl 0
loc 17
ccs 6
cts 8
cp 0.75
crap 4.25
rs 9.8
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.Positive;
23
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
24
import org.checkerframework.common.value.qual.MinLen;
25
import org.checkerframework.dataflow.qual.Pure;
26
27
import java.io.UnsupportedEncodingException;
28
import java.net.URLDecoder;
29
import java.net.URLEncoder;
30
import java.nio.charset.StandardCharsets;
31
import java.security.SecureRandom;
32
33
/**
34
 * Handle key for Dofus packets
35
 */
36
public final class Key {
37
    private static @MonotonicNonNull SecureRandom random;
38
39
    private final @MinLen(1) String key;
0 ignored issues
show
Comprehensibility introduced by
A field should not duplicate the name of its containing class. While technically legal, this practice may be confusing.
Loading history...
40
    private @MonotonicNonNull XorCipher cipher;
41
42 1
    public Key(@MinLen(1) String key) {
43 1
        this.key = key;
44 1
    }
45
46
    @Pure
47
    @Override
48
    public @MinLen(1) String toString() {
49 1
        return key;
50
    }
51
52
    /**
53
     * Get the cipher related to this key
54
     * The cipher instance is saved into the key
55
     *
56
     * @return The cipher instance
57
     */
58
    @Pure
59
    public XorCipher cipher() {
60 1
        if (cipher == null) {
61 1
            cipher = new XorCipher(key);
62
        }
63
64 1
        return cipher;
65
    }
66
67
    /**
68
     * Encode the key to hexadecimal string
69
     *
70
     * @return The encoded key
71
     * @see Key#parse(String) For parse the encoded key
72
     */
73
    @Pure
74
    public String encode() {
75
        final String raw;
76
77
        try {
78 1
            raw = URLEncoder.encode(key, StandardCharsets.UTF_8.toString());
79
        } catch (UnsupportedEncodingException e) {
80
            throw new IllegalStateException("Invalid UTF-8 key", e);
81 1
        }
82
83 1
        final StringBuilder encrypted = new StringBuilder(raw.length() * 2);
84
85 1
        for (int i = 0; i < raw.length(); ++i) {
86 1
            final char c = raw.charAt(i);
87
88 1
            if (c < 16) {
89
                encrypted.append('0');
90
            }
91
92 1
            encrypted.append(Integer.toHexString(c));
93
        }
94
95 1
        return encrypted.toString();
96
    }
97
98
    /**
99
     * Parse an hexadecimal key string
100
     *
101
     * https://github.com/Emudofus/Dofus/blob/1.29/dofus/aks/Aks.as#L232
102
     *
103
     * @param input Key to parse
104
     *
105
     * @return The key instance
106
     *
107
     * @throws IllegalArgumentException When invalid key is given
108
     * @throws NumberFormatException When invalid hexadecimal string is given
109
     *
110
     * @see Key#encode() For generate the hexadecimal string
111
     */
112
    @Pure
113
    @SuppressWarnings({"array.access.unsafe.high", "argument"}) // i / 2 index not resolved, and URLDecoder.decode do not handle string length
114
    public static Key parse(@MinLen(2) String input) {
115 1
        if (input.length() % 2 != 0) {
116 1
            throw new IllegalArgumentException("Invalid key");
117
        }
118
119 1
        final char[] keyArr = new char[input.length() / 2];
120
121 1
        for (int i = 0; i < input.length() - 1; i += 2) {
122 1
            keyArr[i / 2] = (char) Integer.parseInt(input.substring(i, i + 2), 16);
123
        }
124
125
        try {
126 1
            return new Key(URLDecoder.decode(new String(keyArr), StandardCharsets.UTF_8.toString()));
127
        } catch (UnsupportedEncodingException e) {
128
            throw new IllegalArgumentException("Invalid UTF-8 character");
129
        }
130
    }
131
132
    /**
133
     * Generate a new random key, with length of 128 characters
134
     * The generated key contains only displayable characters
135
     *
136
     * @return The generated key
137
     */
138
    public static Key generate() {
139 1
        return generate(128);
140
    }
141
142
    /**
143
     * Generate a new random key
144
     * The generated key contains only displayable characters
145
     *
146
     * @param size The key size
147
     *
148
     * @return The generated key
149
     */
150
    public static Key generate(@Positive int size) {
151 1
        if (random == null) {
152 1
            random = new SecureRandom();
153
        }
154
155 1
        return generate(size, random);
156
    }
157
158
    /**
159
     * Generate a new random key
160
     * The generated key contains only displayable characters
161
     *
162
     * @param size The key size
163
     * @param random The random number generator
164
     *
165
     * @return The generated key
166
     */
167
    public static Key generate(@Positive int size, SecureRandom random) {
168 1
        final char[] keyArr = new char[size];
169
170 1
        for (int i = 0; i < size; ++i) {
171 1
            keyArr[i] = (char) (random.nextInt(127 - 33) + 33);
172
        }
173
174 1
        return new Key(new String(keyArr));
175
    }
176
}
177