1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package javax.crypto.spec;
19
20import java.security.InvalidKeyException;
21import java.security.spec.KeySpec;
22
23/**
24 * The key specification for a DES key.
25 */
26public class DESKeySpec implements KeySpec {
27
28    /**
29     * The length of a DES key in bytes.
30     */
31    public static final int DES_KEY_LEN = 8;
32
33    private final byte[] key;
34
35    // DES weak and semi-weak keys
36    // Got from:
37    // FIP PUB 74
38    // FEDERAL INFORMATION PROCESSING STANDARDS PUBLICATION 1981
39    // GUIDELINES FOR IMPLEMENTING AND USING THE NBS DATA ENCRYPTION STANDARD
40    // http://www.dice.ucl.ac.be/crypto/standards/fips/fip74/fip74-1.pdf
41    private static final byte[][] SEMIWEAKS = {
42                {(byte) 0xE0, (byte) 0x01, (byte) 0xE0, (byte) 0x01,
43                 (byte) 0xF1, (byte) 0x01, (byte) 0xF1, (byte) 0x01},
44
45                {(byte) 0x01, (byte) 0xE0, (byte) 0x01, (byte) 0xE0,
46                 (byte) 0x01, (byte) 0xF1, (byte) 0x01, (byte) 0xF1},
47
48                {(byte) 0xFE, (byte) 0x1F, (byte) 0xFE, (byte) 0x1F,
49                 (byte) 0xFE, (byte) 0x0E, (byte) 0xFE, (byte) 0x0E},
50
51                {(byte) 0x1F, (byte) 0xFE, (byte) 0x1F, (byte) 0xFE,
52                 (byte) 0x0E, (byte) 0xFE, (byte) 0x0E, (byte) 0xFE},
53
54                {(byte) 0xE0, (byte) 0x1F, (byte) 0xE0, (byte) 0x1F,
55                 (byte) 0xF1, (byte) 0x0E, (byte) 0xF1, (byte) 0x0E},
56
57                {(byte) 0x1F, (byte) 0xE0, (byte) 0x1F, (byte) 0xE0,
58                 (byte) 0x0E, (byte) 0xF1, (byte) 0x0E, (byte) 0xF1},
59
60                {(byte) 0x01, (byte) 0xFE, (byte) 0x01, (byte) 0xFE,
61                 (byte) 0x01, (byte) 0xFE, (byte) 0x01, (byte) 0xFE},
62
63                {(byte) 0xFE, (byte) 0x01, (byte) 0xFE, (byte) 0x01,
64                 (byte) 0xFE, (byte) 0x01, (byte) 0xFE, (byte) 0x01},
65
66                {(byte) 0x01, (byte) 0x1F, (byte) 0x01, (byte) 0x1F,
67                 (byte) 0x01, (byte) 0x0E, (byte) 0x01, (byte) 0x0E},
68
69                {(byte) 0x1F, (byte) 0x01, (byte) 0x1F, (byte) 0x01,
70                 (byte) 0x0E, (byte) 0x01, (byte) 0x0E, (byte) 0x01},
71
72                {(byte) 0xE0, (byte) 0xFE, (byte) 0xE0, (byte) 0xFE,
73                 (byte) 0xF1, (byte) 0xFE, (byte) 0xF1, (byte) 0xFE},
74
75                {(byte) 0xFE, (byte) 0xE0, (byte) 0xFE, (byte) 0xE0,
76                 (byte) 0xFE, (byte) 0xF1, (byte) 0xFE, (byte) 0xF1},
77
78                {(byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01,
79                 (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01},
80
81                {(byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE,
82                 (byte) 0xFE, (byte) 0xFE, (byte) 0xFE, (byte) 0xFE},
83
84                {(byte) 0xE0, (byte) 0xE0, (byte) 0xE0, (byte) 0xE0,
85                 (byte) 0xF1, (byte) 0xF1, (byte) 0xF1, (byte) 0xF1},
86
87                {(byte) 0x1F, (byte) 0x1F, (byte) 0x1F, (byte) 0x1F,
88                 (byte) 0x0E, (byte) 0x0E, (byte) 0x0E, (byte) 0x0E},
89
90                };
91
92    /**
93     * Creates a new <code>DESKeySpec</code> from the first 8 bytes of the
94     * specified key data.
95     *
96     * @param key
97     *            the key data.
98     * @throws InvalidKeyException
99     *             if the length of the specified key data is less than 8.
100     */
101    public DESKeySpec(byte[] key) throws InvalidKeyException {
102        this(key, 0);
103    }
104
105    /**
106     * Creates a new <code>DESKeySpec</code> from the first 8 bytes of the
107     * specified key data starting at <code>offset</code>.
108     *
109     * @param key
110     *            the key data
111     * @param offset
112     *            the offset to start at.
113     * @throws InvalidKeyException
114     *             if the length of the specified key data starting at offset is
115     *             less than 8.
116     */
117    public DESKeySpec(byte[] key, int offset) throws InvalidKeyException {
118        if (key == null) {
119            throw new NullPointerException("key == null");
120        }
121        if (key.length - offset < DES_KEY_LEN) {
122            throw new InvalidKeyException("key too short");
123        }
124        this.key = new byte[DES_KEY_LEN];
125        System.arraycopy(key, offset, this.key, 0, DES_KEY_LEN);
126    }
127
128    /**
129     * Returns a copy of the key.
130     *
131     * @return a copy of the key.
132     */
133    public byte[] getKey() {
134        byte[] result = new byte[DES_KEY_LEN];
135        System.arraycopy(this.key, 0, result, 0, DES_KEY_LEN);
136        return result;
137    }
138
139    /**
140     * Returns whether the specified key data starting at <code>offset</code> is
141     * <i>parity-adjusted</i>.
142     *
143     * @param key
144     *            the key data.
145     * @param offset
146     *            the offset to start checking at.
147     * @return {@code true} if the specified key data is parity-adjusted,
148     *            {@code false} otherwise.
149     * @throws InvalidKeyException
150     *             if the length of the key data starting at offset is less than
151     *             8, or the key is null.
152     */
153    public static boolean isParityAdjusted(byte[] key, int offset) throws InvalidKeyException {
154        if (key == null) {
155            throw new InvalidKeyException("key == null");
156        }
157        if (key.length - offset < DES_KEY_LEN) {
158            throw new InvalidKeyException("key too short");
159        }
160
161        int byteKey = 0;
162
163        for (int i = offset; i < DES_KEY_LEN; i++) {
164            byteKey = key[i];
165
166            byteKey ^= byteKey >> 1;
167            byteKey ^= byteKey >> 2;
168            byteKey ^= byteKey >> 4;
169
170            if ((byteKey & 1) == 0) {
171                return false;
172            }
173        }
174        return true;
175    }
176
177    /**
178     * Returns whether the specified key data starting at <code>offset</code> is
179     * weak or semi-weak.
180     *
181     * @param key
182     *            the key data.
183     * @param offset
184     *            the offset to start checking at.
185     * @return {@code true} if the specified key data is weak or semi-weak.
186     * @throws InvalidKeyException
187     *             if the length of the key data starting at offset is less than
188     *             8, or it is null.
189     */
190    public static boolean isWeak(byte[] key, int offset) throws InvalidKeyException {
191        if (key == null) {
192            throw new InvalidKeyException("key == null");
193        }
194        if (key.length - offset < DES_KEY_LEN) {
195            throw new InvalidKeyException("key too short");
196        }
197        I:
198        for (int i=0; i<SEMIWEAKS.length; i++) {
199            for (int j=0; j<DES_KEY_LEN; j++) {
200                if (SEMIWEAKS[i][j] != key[offset+j]) {
201                    continue I;
202                }
203            }
204            return true;
205        }
206        return false;
207    }
208}
209