1 /**
2  * This file is part of the dcrypt project.
3  *
4  * Copyright:
5  *     Copyright (C) dcrypt contributors 2009.
6  *     Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH.
7  *     All rights reserved.
8  *
9  * License:
10  *     Tango Dual License: 3-Clause BSD License / Academic Free License v3.0.
11  *     See LICENSE_TANGO.txt for details.
12  *
13  * Authors: Thomas Dixon
14  *
15  */
16 
17 module ocean.util.cipher.misc.ByteConverter;
18 
19 import ocean.meta.types.Qualifiers;
20 
21 version (unittest)
22 {
23     import ocean.core.Test;
24 }
25 
26 /** Converts between integral types and unsigned byte arrays */
27 struct ByteConverter
28 {
29     private enum istring hexits = "0123456789abcdef";
30 
31     /** Conversions between little endian integrals and bytes */
32     struct LittleEndian
33     {
34         /**
35          * Converts the supplied array to integral type T
36          *
37          * Params:
38          *     x_ = The supplied array of bytes (ubytes, bytes, chars, whatever)
39          *
40          * Returns:
41          *     A integral of type T created with the supplied bytes placed
42          *     in the specified byte order.
43          */
44         static T to (T) (const(void)[] x_)
45         {
46             auto x = cast(const(ubyte)[])x_;
47 
48             T result = ((x[0] & 0xff)       |
49                        ((x[1] & 0xff) << 8));
50 
51             static if (T.sizeof >= int.sizeof)
52             {
53                 result |= ((x[2] & 0xff) << 16) |
54                           ((x[3] & 0xff) << 24);
55             }
56 
57             static if (T.sizeof >= long.sizeof)
58             {
59                 result |= (cast(T)(x[4] & 0xff) << 32) |
60                           (cast(T)(x[5] & 0xff) << 40) |
61                           (cast(T)(x[6] & 0xff) << 48) |
62                           (cast(T)(x[7] & 0xff) << 56);
63             }
64 
65             return result;
66         }
67 
68         /**
69          * Converts the supplied integral to an array of unsigned bytes.
70          *
71          * Params:
72          *     input = Integral to convert to bytes
73          *
74          * Returns:
75          *     Integral input of type T split into its respective bytes
76          *     with the bytes placed in the specified byte order.
77          */
78         static ubyte[] from (T) (const(T) input)
79         {
80             ubyte[] output = new ubyte[T.sizeof];
81 
82             output[0] = cast(ubyte)(input);
83             output[1] = cast(ubyte)(input >> 8);
84 
85             static if (T.sizeof >= int.sizeof)
86             {
87                 output[2] = cast(ubyte)(input >> 16);
88                 output[3] = cast(ubyte)(input >> 24);
89             }
90 
91             static if (T.sizeof >= long.sizeof)
92             {
93                 output[4] = cast(ubyte)(input >> 32);
94                 output[5] = cast(ubyte)(input >> 40);
95                 output[6] = cast(ubyte)(input >> 48);
96                 output[7] = cast(ubyte)(input >> 56);
97             }
98 
99             return output;
100         }
101     }
102 
103     /** Conversions between big endian integrals and bytes */
104     struct BigEndian
105     {
106 
107         static T to (T) (const(void)[] x_)
108         {
109             auto x = cast(const(ubyte)[])x_;
110 
111             static if (is(T == ushort) || is(T == short))
112             {
113                 return cast(T) (((x[0] & 0xff) << 8) |
114                                  (x[1] & 0xff));
115             }
116             else static if (is(T == uint) || is(T == int))
117             {
118                 return cast(T) (((x[0] & 0xff) << 24) |
119                                 ((x[1] & 0xff) << 16) |
120                                 ((x[2] & 0xff) << 8)  |
121                                  (x[3] & 0xff));
122             }
123             else static if (is(T == ulong) || is(T == long))
124             {
125                 return cast(T) ((cast(T)(x[0] & 0xff) << 56) |
126                                 (cast(T)(x[1] & 0xff) << 48) |
127                                 (cast(T)(x[2] & 0xff) << 40) |
128                                 (cast(T)(x[3] & 0xff) << 32) |
129                                 ((x[4] & 0xff) << 24) |
130                                 ((x[5] & 0xff) << 16) |
131                                 ((x[6] & 0xff) << 8)  |
132                                  (x[7] & 0xff));
133             }
134         }
135 
136         static ubyte[] from(T)(T input)
137         {
138             ubyte[] output = new ubyte[T.sizeof];
139 
140             static if (T.sizeof == long.sizeof)
141             {
142                 output[0] = cast(ubyte)(input >> 56);
143                 output[1] = cast(ubyte)(input >> 48);
144                 output[2] = cast(ubyte)(input >> 40);
145                 output[3] = cast(ubyte)(input >> 32);
146                 output[4] = cast(ubyte)(input >> 24);
147                 output[5] = cast(ubyte)(input >> 16);
148                 output[6] = cast(ubyte)(input >> 8);
149                 output[7] = cast(ubyte)(input);
150             }
151             else static if (T.sizeof == int.sizeof)
152             {
153                 output[0] = cast(ubyte)(input >> 24);
154                 output[1] = cast(ubyte)(input >> 16);
155                 output[2] = cast(ubyte)(input >> 8);
156                 output[3] = cast(ubyte)(input);
157             }
158             else static if (T.sizeof == short.sizeof)
159             {
160                 output[0] = cast(ubyte)(input >> 8);
161                 output[1] = cast(ubyte)(input);
162             }
163 
164             return output;
165         }
166     }
167 
168     /**
169      * Takes an array and converts each byte to its hex representation.
170      *
171      * Params:
172      *     input_ = the array of bytes to represent
173      *
174      * Returns:
175      *     A newed char[] containing the hex digits representing the
176      *     input
177      */
178 
179     static mstring hexEncode(const(void)[] input_)
180     {
181         mstring buffer;
182 
183         return(hexEncode(input_, buffer));
184     }
185 
186     /**
187      * Takes an array and converts each byte to its hex representation.
188      *
189      * Params:
190      *     input_ = the array of bytes to represent
191      *     output = the buffer into which the results will be written
192      *
193      * Returns:
194      *     A slice of output containing the hex digits representing the
195      *     input
196      */
197 
198     static mstring hexEncode(const(void)[] input_, ref mstring output)
199     {
200         auto input = cast(const(ubyte)[])input_;
201         // make sure our buffer is big enough (2 hex digits per byte).
202         output.length = input.length * 2;
203 
204         int i = 0;
205         foreach (ubyte j; input)
206         {
207             output[i++] = hexits[j>>4];
208             output[i++] = hexits[j&0xf];
209         }
210 
211         return output;
212     }
213 
214     unittest
215     {
216         mstring buffer;
217 
218         test!("==")(hexEncode(cast(ubyte[])([
219                         0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef
220                     ]), buffer), "0123456789abcdef"[]);
221         // check the right amount of memory was allocated
222         test!("==")(buffer.length, 16);
223     }
224 
225     static ubyte[] hexDecode(cstring input)
226     {
227         cstring inputAsLower = stringToLower(input);
228         ubyte[] output = new ubyte[input.length>>1];
229 
230         static ubyte[char] hexitIndex;
231         for (ubyte i = 0; i < hexits.length; i++)
232             hexitIndex[hexits[i]] = i;
233 
234         for (int i = 0, j = 0; i < output.length; i++)
235         {
236             output[i] = cast(ubyte) (hexitIndex[inputAsLower[j++]] << 4);
237             output[i] |= hexitIndex[inputAsLower[j++]];
238         }
239 
240         return output;
241     }
242 
243     private static mstring stringToLower(cstring input)
244     {
245         mstring output = new char[input.length];
246 
247         foreach (size_t i, char c; input)
248             output[i] = cast(ubyte) ((c >= 'A' && c <= 'Z') ? c+32 : c);
249 
250         return output;
251     }
252 }