1 /**
2 * Base class for message authentiction codes
3 * 
4 * Copyright:
5 * (C) 1999-2007 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.mac.mac;
12 
13 import botan.constants;
14 public import botan.algo_base.sym_algo;
15 public import botan.algo_base.buf_comp;
16 import botan.utils.mem_ops;
17 
18 /**
19 * This class represents Message Authentication Code (MAC) objects.
20 */
21 interface MessageAuthenticationCode : BufferedComputation, SymmetricAlgorithm
22 {
23 public:
24     /**
25     * Verify a MAC.
26     *
27     * Params:
28     *  mac = the MAC to verify as a ubyte array
29     *  length = the length of param in
30     * Returns: true if the MAC is valid, false otherwise
31     */
32     final bool verifyMac(const(ubyte)* mac, size_t length)
33     {
34         SecureVector!ubyte our_mac = finished();
35         
36         if (our_mac.length != length)
37             return false;
38         
39         return sameMem(our_mac.ptr, mac, length);
40     }
41 
42     /**
43     * Get a new object representing the same algorithm as this
44     */
45     abstract MessageAuthenticationCode clone() const;
46 
47     /**
48     * Get the name of this algorithm.
49     * Returns: name of this algorithm
50     */
51     abstract @property string name() const;
52 }
53 
54 static if (BOTAN_TEST):
55 
56 import botan.test;
57 import botan.libstate.libstate;
58 import botan.codec.hex;
59 import memutils.hashmap;
60 import core.atomic;
61 
62 private shared size_t total_tests;
63 
64 size_t macTest(string algo, string key_hex, string in_hex, string out_hex)
65 {
66     AlgorithmFactory af = globalState().algorithmFactory();
67     
68     const auto providers = af.providersOf(algo);
69     size_t fails = 0;
70 
71     atomicOp!"+="(total_tests, 1);
72     if (providers.empty)
73     {
74         logError("Unknown algo " ~ algo);
75         ++fails;
76     }
77     
78     foreach (provider; providers[])
79     {
80         atomicOp!"+="(total_tests, 1);
81         auto proto = af.prototypeMac(algo, provider);
82         
83         if (!proto)
84         {
85             logError("Unable to get " ~ algo ~ " from " ~ provider);
86             ++fails;
87             continue;
88         }
89         
90         Unique!MessageAuthenticationCode mac = proto.clone();
91         
92         mac.setKey(hexDecode(key_hex));
93         mac.update(hexDecode(in_hex));
94         
95         auto h = mac.finished();
96 
97         atomicOp!"+="(total_tests, 1);
98         if (h != hexDecodeLocked(out_hex))
99         {
100             logError(algo ~ " " ~ provider ~ " got " ~ hexEncode(h) ~ " != " ~ out_hex);
101             ++fails;
102         }
103     }
104     
105     return fails;
106 }
107 
108 static if (BOTAN_HAS_TESTS && !SKIP_MAC_TEST) unittest {  
109     logDebug("Testing mac.d ...");  
110     auto test = delegate(string input) {
111         File vec = File(input, "r");
112         
113         return runTestsBb(vec, "Mac", "Out", true,
114             (ref HashMap!(string, string) m) {
115                 return macTest(m["Mac"], m["Key"], m["In"], m["Out"]);
116             });
117     };
118     
119     size_t fails = runTestsInDir("test_data/mac", test);
120 
121     testReport("mac", total_tests, fails);
122 }