source: trunk/src-cryptopp/seckey.h

Last change on this file was e230cb0, checked in by David Stainton <dstainton415@…>, at 2016-10-12T13:27:29Z

Add cryptopp from tag CRYPTOPP_5_6_5

  • Property mode set to 100644
File size: 21.0 KB
Line 
1// seckey.h - written and placed in the public domain by Wei Dai
2
3//! \file
4//! \brief Classes and functions for implementing secret key algorithms.
5
6#ifndef CRYPTOPP_SECKEY_H
7#define CRYPTOPP_SECKEY_H
8
9#include "config.h"
10
11#if CRYPTOPP_MSC_VERSION
12# pragma warning(push)
13# pragma warning(disable: 4189)
14#endif
15
16#include "cryptlib.h"
17#include "misc.h"
18#include "simple.h"
19
20NAMESPACE_BEGIN(CryptoPP)
21
22//! \brief Inverts the cipher's direction
23//! \param dir the cipher's direction
24//! \returns DECRYPTION if \ref CipherDir "dir" is ENCRYPTION, DECRYPTION otherwise
25inline CipherDir ReverseCipherDir(CipherDir dir)
26{
27        return (dir == ENCRYPTION) ? DECRYPTION : ENCRYPTION;
28}
29
30//! \class FixedBlockSize
31//! \brief Inherited by algorithms with fixed block size
32//! \tparam N the blocksize of the algorithm
33template <unsigned int N>
34class FixedBlockSize
35{
36public:
37        //! \brief The block size of the algorithm provided as a constant.
38        CRYPTOPP_CONSTANT(BLOCKSIZE = N)
39};
40
41// ************** rounds ***************
42
43//! \class FixedRounds
44//! \brief Inherited by algorithms with fixed number of rounds
45//! \tparam R the number of rounds used by the algorithm
46template <unsigned int R>
47class FixedRounds
48{
49public:
50        //! \brief The number of rounds for the algorithm provided as a constant.
51        CRYPTOPP_CONSTANT(ROUNDS = R)
52};
53
54//! \class VariableRounds
55//! \brief Inherited by algorithms with variable number of rounds
56//! \tparam D Default number of rounds
57//! \tparam N Minimum number of rounds
58//! \tparam D Maximum number of rounds
59template <unsigned int D, unsigned int N=1, unsigned int M=INT_MAX>             // use INT_MAX here because enums are treated as signed ints
60class VariableRounds
61{
62public:
63        //! \brief The default number of rounds for the algorithm provided as a constant.
64        CRYPTOPP_CONSTANT(DEFAULT_ROUNDS = D)
65        //! \brief The minimum number of rounds for the algorithm provided as a constant.
66        CRYPTOPP_CONSTANT(MIN_ROUNDS = N)
67        //! \brief The maximum number of rounds for the algorithm provided as a constant.
68        CRYPTOPP_CONSTANT(MAX_ROUNDS = M)
69        //! \brief The default number of rounds for the algorithm based on key length
70        //!   provided by a static function.
71        //! \param keylength the size of the key, in bytes
72        //! \details keylength is unused in the default implementation.
73        CRYPTOPP_CONSTEXPR static unsigned int StaticGetDefaultRounds(size_t keylength)
74        {
75                // Comma operator breaks Debug builds with GCC 4.0 - 4.6.
76                // Also see http://github.com/weidai11/cryptopp/issues/255
77#if defined(CRYPTOPP_CXX11_CONSTEXPR)
78                return CRYPTOPP_UNUSED(keylength), static_cast<unsigned int>(DEFAULT_ROUNDS);
79#else
80                CRYPTOPP_UNUSED(keylength);
81                return static_cast<unsigned int>(DEFAULT_ROUNDS);
82#endif
83        }
84
85protected:
86        //! \brief Validates the number of rounds for an algorithm.
87        //! \param rounds the candidate number of rounds
88        //! \param alg an Algorithm object used if the number of rounds are invalid
89        //! \throws InvalidRounds if the number of rounds are invalid
90        //! \details ThrowIfInvalidRounds() validates the number of rounds and throws if invalid.
91        inline void ThrowIfInvalidRounds(int rounds, const Algorithm *alg)
92        {
93                if (M == INT_MAX) // Coverity and result_independent_of_operands
94                {
95                        if (rounds < MIN_ROUNDS)
96                                throw InvalidRounds(alg ? alg->AlgorithmName() : std::string("VariableRounds"), rounds);
97                }
98                else
99                {
100                        if (rounds < MIN_ROUNDS || rounds > MAX_ROUNDS)
101                                throw InvalidRounds(alg ? alg->AlgorithmName() : std::string("VariableRounds"), rounds);
102                }
103        }
104
105        //! \brief Validates the number of rounds for an algorithm
106        //! \param param the candidate number of rounds
107        //! \param alg an Algorithm object used if the number of rounds are invalid
108        //! \returns the number of rounds for the algorithm
109        //! \throws InvalidRounds if the number of rounds are invalid
110        //! \details GetRoundsAndThrowIfInvalid() validates the number of rounds and throws if invalid.
111        inline unsigned int GetRoundsAndThrowIfInvalid(const NameValuePairs &param, const Algorithm *alg)
112        {
113                int rounds = param.GetIntValueWithDefault("Rounds", DEFAULT_ROUNDS);
114                ThrowIfInvalidRounds(rounds, alg);
115                return (unsigned int)rounds;
116        }
117};
118
119// ************** key length ***************
120
121//! \class FixedKeyLength
122//! \brief Inherited by keyed algorithms with fixed key length
123//! \tparam N Default key length, in bytes
124//! \tparam IV_REQ the \ref SimpleKeyingInterface::IV_Requirement "IV requirements"
125//! \tparam IV_L default IV length, in bytes
126//! \sa SimpleKeyingInterface
127template <unsigned int N, unsigned int IV_REQ = SimpleKeyingInterface::NOT_RESYNCHRONIZABLE, unsigned int IV_L = 0>
128class FixedKeyLength
129{
130public:
131        //! \brief The default key length used by the algorithm provided as a constant
132        //! \details KEYLENGTH is provided in bytes, not bits
133        CRYPTOPP_CONSTANT(KEYLENGTH=N)
134        //! \brief The minimum key length used by the algorithm provided as a constant
135        //! \details MIN_KEYLENGTH is provided in bytes, not bits
136        CRYPTOPP_CONSTANT(MIN_KEYLENGTH=N)
137        //! \brief The maximum key length used by the algorithm provided as a constant
138        //! \details MAX_KEYLENGTH is provided in bytes, not bits
139        CRYPTOPP_CONSTANT(MAX_KEYLENGTH=N)
140        //! \brief The default key length used by the algorithm provided as a constant
141        //! \details DEFAULT_KEYLENGTH is provided in bytes, not bits
142        CRYPTOPP_CONSTANT(DEFAULT_KEYLENGTH=N)
143        //! \brief The default IV requirements for the algorithm provided as a constant
144        //! \details The default value is NOT_RESYNCHRONIZABLE. See IV_Requirement
145        //!  in cryptlib.h for allowed values.
146        CRYPTOPP_CONSTANT(IV_REQUIREMENT = IV_REQ)
147        //! \brief The default IV length used by the algorithm provided as a constant
148        //! \details IV_LENGTH is provided in bytes, not bits. The default implementation uses 0.
149        CRYPTOPP_CONSTANT(IV_LENGTH = IV_L)
150        //! \brief The default key length for the algorithm provided by a static function.
151        //! \param keylength the size of the key, in bytes
152        //! \details The default implementation returns KEYLENGTH. keylength is unused
153        //!   in the default implementation.
154        CRYPTOPP_CONSTEXPR static size_t CRYPTOPP_API StaticGetValidKeyLength(size_t keylength)
155        {
156                // Comma operator breaks Debug builds with GCC 4.0 - 4.6.
157                // Also see http://github.com/weidai11/cryptopp/issues/255
158#if defined(CRYPTOPP_CXX11_CONSTEXPR)
159                return CRYPTOPP_UNUSED(keylength), static_cast<size_t>(KEYLENGTH);
160#else
161                CRYPTOPP_UNUSED(keylength);
162                return static_cast<size_t>(KEYLENGTH);
163#endif
164        }
165};
166
167//! \class VariableKeyLength
168//! \brief Inherited by keyed algorithms with variable key length
169//! \tparam D Default key length, in bytes
170//! \tparam N Minimum key length, in bytes
171//! \tparam M Maximum key length, in bytes
172//! \tparam M Default key length multiple, in bytes. The default multiple is 1.
173//! \tparam IV_REQ the \ref SimpleKeyingInterface::IV_Requirement "IV requirements"
174//! \tparam IV_L default IV length, in bytes. The default length is 0.
175//! \sa SimpleKeyingInterface
176template <unsigned int D, unsigned int N, unsigned int M, unsigned int Q = 1, unsigned int IV_REQ = SimpleKeyingInterface::NOT_RESYNCHRONIZABLE, unsigned int IV_L = 0>
177class VariableKeyLength
178{
179        // Make these private to avoid Doxygen documenting them in all derived classes
180        CRYPTOPP_COMPILE_ASSERT(Q > 0);
181        CRYPTOPP_COMPILE_ASSERT(N % Q == 0);
182        CRYPTOPP_COMPILE_ASSERT(M % Q == 0);
183        CRYPTOPP_COMPILE_ASSERT(N < M);
184        CRYPTOPP_COMPILE_ASSERT(D >= N);
185        CRYPTOPP_COMPILE_ASSERT(M >= D);
186
187public:
188        //! \brief The minimum key length used by the algorithm provided as a constant
189        //! \details MIN_KEYLENGTH is provided in bytes, not bits
190        CRYPTOPP_CONSTANT(MIN_KEYLENGTH=N)
191        //! \brief The maximum key length used by the algorithm provided as a constant
192        //! \details MAX_KEYLENGTH is provided in bytes, not bits
193        CRYPTOPP_CONSTANT(MAX_KEYLENGTH=M)
194        //! \brief The default key length used by the algorithm provided as a constant
195        //! \details DEFAULT_KEYLENGTH is provided in bytes, not bits
196        CRYPTOPP_CONSTANT(DEFAULT_KEYLENGTH=D)
197        //! \brief The key length multiple used by the algorithm provided as a constant
198        //! \details MAX_KEYLENGTH is provided in bytes, not bits
199        CRYPTOPP_CONSTANT(KEYLENGTH_MULTIPLE=Q)
200        //! \brief The default IV requirements for the algorithm provided as a constant
201        //! \details The default value is NOT_RESYNCHRONIZABLE. See IV_Requirement
202        //!  in cryptlib.h for allowed values.
203        CRYPTOPP_CONSTANT(IV_REQUIREMENT=IV_REQ)
204        //! \brief The default initialization vector length for the algorithm provided as a constant
205        //! \details IV_LENGTH is provided in bytes, not bits. The default implementation uses 0.
206        CRYPTOPP_CONSTANT(IV_LENGTH=IV_L)
207        //! \brief Provides a valid key length for the algorithm provided by a static function.
208        //! \param keylength the size of the key, in bytes
209        //! \details If keylength is less than MIN_KEYLENGTH, then the function returns
210        //!   MIN_KEYLENGTH. If keylength is greater than MAX_KEYLENGTH, then the function
211        //!   returns MAX_KEYLENGTH. If keylength is a multiple of KEYLENGTH_MULTIPLE,
212        //!   then keylength is returned. Otherwise, the function returns keylength rounded
213        //!   \a down to the next smaller multiple of KEYLENGTH_MULTIPLE.
214        //! \details keylength is provided in bytes, not bits.
215        // TODO: Figure out how to make this CRYPTOPP_CONSTEXPR
216        static size_t CRYPTOPP_API StaticGetValidKeyLength(size_t keylength)
217        {
218                if (keylength < (size_t)MIN_KEYLENGTH)
219                        return MIN_KEYLENGTH;
220                else if (keylength > (size_t)MAX_KEYLENGTH)
221                        return (size_t)MAX_KEYLENGTH;
222                else
223                {
224                        keylength += KEYLENGTH_MULTIPLE-1;
225                        return keylength - keylength%KEYLENGTH_MULTIPLE;
226                }
227        }
228};
229
230//! \class SameKeyLengthAs
231//! \brief Provides key lengths based on another class's key length
232//! \tparam T another FixedKeyLength or VariableKeyLength class
233//! \tparam IV_REQ the \ref SimpleKeyingInterface::IV_Requirement "IV requirements"
234//! \tparam IV_L default IV length, in bytes
235//! \sa SimpleKeyingInterface
236template <class T, unsigned int IV_REQ = SimpleKeyingInterface::NOT_RESYNCHRONIZABLE, unsigned int IV_L = 0>
237class SameKeyLengthAs
238{
239public:
240        //! \brief The minimum key length used by the algorithm provided as a constant
241        //! \details MIN_KEYLENGTH is provided in bytes, not bits
242        CRYPTOPP_CONSTANT(MIN_KEYLENGTH=T::MIN_KEYLENGTH)
243        //! \brief The maximum key length used by the algorithm provided as a constant
244        //! \details MIN_KEYLENGTH is provided in bytes, not bits
245        CRYPTOPP_CONSTANT(MAX_KEYLENGTH=T::MAX_KEYLENGTH)
246        //! \brief The default key length used by the algorithm provided as a constant
247        //! \details MIN_KEYLENGTH is provided in bytes, not bits
248        CRYPTOPP_CONSTANT(DEFAULT_KEYLENGTH=T::DEFAULT_KEYLENGTH)
249        //! \brief The default IV requirements for the algorithm provided as a constant
250        //! \details The default value is NOT_RESYNCHRONIZABLE. See IV_Requirement
251        //!  in cryptlib.h for allowed values.
252        CRYPTOPP_CONSTANT(IV_REQUIREMENT=IV_REQ)
253        //! \brief The default initialization vector length for the algorithm provided as a constant
254        //! \details IV_LENGTH is provided in bytes, not bits. The default implementation uses 0.
255        CRYPTOPP_CONSTANT(IV_LENGTH=IV_L)
256        //! \brief Provides a valid key length for the algorithm provided by a static function.
257        //! \param keylength the size of the key, in bytes
258        //! \details If keylength is less than MIN_KEYLENGTH, then the function returns
259        //!   MIN_KEYLENGTH. If keylength is greater than MAX_KEYLENGTH, then the function
260        //!   returns MAX_KEYLENGTH. If keylength is a multiple of KEYLENGTH_MULTIPLE,
261        //!   then keylength is returned. Otherwise, the function returns keylength rounded
262        //!   \a down to the next smaller multiple of KEYLENGTH_MULTIPLE.
263        //! \details keylength is provided in bytes, not bits.
264        CRYPTOPP_CONSTEXPR static size_t CRYPTOPP_API StaticGetValidKeyLength(size_t keylength)
265                {return T::StaticGetValidKeyLength(keylength);}
266};
267
268// ************** implementation helper for SimpleKeyingInterface ***************
269
270//! \class SimpleKeyingInterfaceImpl
271//! \brief Provides a base implementation of SimpleKeyingInterface
272//! \tparam BASE a SimpleKeyingInterface derived class
273//! \tparam INFO a SimpleKeyingInterface derived class
274//! \details SimpleKeyingInterfaceImpl() provides a default implementation for ciphers providing a keying interface.
275//!   Functions are virtual and not eligible for C++11 <tt>constexpr</tt>-ness.
276//! \sa Algorithm(), SimpleKeyingInterface()
277template <class BASE, class INFO = BASE>
278class CRYPTOPP_NO_VTABLE SimpleKeyingInterfaceImpl : public BASE
279{
280public:
281        //! \brief The minimum key length used by the algorithm
282        //! \returns minimum key length used by the algorithm, in bytes
283        size_t MinKeyLength() const
284                {return INFO::MIN_KEYLENGTH;}
285
286        //! \brief The maximum key length used by the algorithm
287        //! \returns maximum key length used by the algorithm, in bytes
288        size_t MaxKeyLength() const
289                {return (size_t)INFO::MAX_KEYLENGTH;}
290
291        //! \brief The default key length used by the algorithm
292        //! \returns default key length used by the algorithm, in bytes
293        size_t DefaultKeyLength() const
294                {return INFO::DEFAULT_KEYLENGTH;}
295
296        //! \brief Provides a valid key length for the algorithm
297        //! \param keylength the size of the key, in bytes
298        //! \returns the valid key lenght, in bytes
299        //! \details keylength is provided in bytes, not bits. If keylength is less than MIN_KEYLENGTH,
300        //!   then the function returns MIN_KEYLENGTH. If keylength is greater than MAX_KEYLENGTH,
301        //!   then the function returns MAX_KEYLENGTH. if If keylength is a multiple of KEYLENGTH_MULTIPLE,
302        //!   then keylength is returned. Otherwise, the function returns a \a lower multiple of
303        //!   KEYLENGTH_MULTIPLE.
304        size_t GetValidKeyLength(size_t keylength) const {return INFO::StaticGetValidKeyLength(keylength);}
305
306        //! \brief The default IV requirements for the algorithm
307        //! \details The default value is NOT_RESYNCHRONIZABLE. See IV_Requirement
308        //!  in cryptlib.h for allowed values.
309        SimpleKeyingInterface::IV_Requirement IVRequirement() const
310                {return (SimpleKeyingInterface::IV_Requirement)INFO::IV_REQUIREMENT;}
311
312        //! \brief The default initialization vector length for the algorithm
313        //! \details IVSize is provided in bytes, not bits. The default implementation uses IV_LENGTH, which is 0.
314        unsigned int IVSize() const
315                {return INFO::IV_LENGTH;}
316};
317
318//! \class BlockCipherImpl
319//! \brief Provides a base implementation of Algorithm and SimpleKeyingInterface for block ciphers
320//! \tparam INFO a SimpleKeyingInterface derived class
321//! \tparam BASE a SimpleKeyingInterface derived class
322//! \details BlockCipherImpl() provides a default implementation for block ciphers using AlgorithmImpl()
323//!   and SimpleKeyingInterfaceImpl(). Functions are virtual and not eligible for C++11 <tt>constexpr</tt>-ness.
324//! \sa Algorithm(), SimpleKeyingInterface(), AlgorithmImpl(), SimpleKeyingInterfaceImpl()
325template <class INFO, class BASE = BlockCipher>
326class CRYPTOPP_NO_VTABLE BlockCipherImpl : public AlgorithmImpl<SimpleKeyingInterfaceImpl<TwoBases<BASE, INFO> > >
327{
328public:
329        //! Provides the block size of the algorithm
330        //! \returns the block size of the algorithm, in bytes
331        unsigned int BlockSize() const {return this->BLOCKSIZE;}
332};
333
334//! \class BlockCipherFinal
335//! \brief Provides class member functions to key a block cipher
336//! \tparam DIR a CipherDir
337//! \tparam BASE a BlockCipherImpl derived class
338template <CipherDir DIR, class BASE>
339class BlockCipherFinal : public ClonableImpl<BlockCipherFinal<DIR, BASE>, BASE>
340{
341public:
342        //! \brief Construct a default BlockCipherFinal
343        //! \details The cipher is not keyed.
344        BlockCipherFinal() {}
345
346        //! \brief Construct a BlockCipherFinal
347        //! \param key a byte array used to key the cipher
348        //! \details key must be at least DEFAULT_KEYLENGTH in length. Internally, the function calls
349        //!    SimpleKeyingInterface::SetKey.
350        BlockCipherFinal(const byte *key)
351                {this->SetKey(key, this->DEFAULT_KEYLENGTH);}
352
353        //! \brief Construct a BlockCipherFinal
354        //! \param key a byte array used to key the cipher
355        //! \param length the length of the byte array
356        //! \details key must be at least DEFAULT_KEYLENGTH in length. Internally, the function calls
357        //!    SimpleKeyingInterface::SetKey.
358        BlockCipherFinal(const byte *key, size_t length)
359                {this->SetKey(key, length);}
360
361        //! \brief Construct a BlockCipherFinal
362        //! \param key a byte array used to key the cipher
363        //! \param length the length of the byte array
364        //! \param rounds the number of rounds
365        //! \details key must be at least DEFAULT_KEYLENGTH in length. Internally, the function calls
366        //!    SimpleKeyingInterface::SetKeyWithRounds.
367        BlockCipherFinal(const byte *key, size_t length, unsigned int rounds)
368                {this->SetKeyWithRounds(key, length, rounds);}
369
370        //! \brief Provides the direction of the cipher
371        //! \returns true if DIR is ENCRYPTION, false otherwise
372        //! \sa GetCipherDirection(), IsPermutation()
373        bool IsForwardTransformation() const {return DIR == ENCRYPTION;}
374};
375
376//! \class MessageAuthenticationCodeImpl
377//! \brief Provides a base implementation of Algorithm and SimpleKeyingInterface for message authentication codes
378//! \tparam INFO a SimpleKeyingInterface derived class
379//! \tparam BASE a SimpleKeyingInterface derived class
380//! \details MessageAuthenticationCodeImpl() provides a default implementation for message authentication codes
381//!   using AlgorithmImpl() and SimpleKeyingInterfaceImpl(). Functions are virtual and not subject to C++11
382//!   <tt>constexpr</tt>.
383//! \sa Algorithm(), SimpleKeyingInterface(), AlgorithmImpl(), SimpleKeyingInterfaceImpl()
384template <class BASE, class INFO = BASE>
385class MessageAuthenticationCodeImpl : public AlgorithmImpl<SimpleKeyingInterfaceImpl<BASE, INFO>, INFO>
386{
387};
388
389//! \class MessageAuthenticationCodeFinal
390//! \brief Provides class member functions to key a message authentication code
391//! \tparam DIR a CipherDir
392//! \tparam BASE a BlockCipherImpl derived class
393template <class BASE>
394class MessageAuthenticationCodeFinal : public ClonableImpl<MessageAuthenticationCodeFinal<BASE>, MessageAuthenticationCodeImpl<BASE> >
395{
396public:
397        //! \brief Construct a default MessageAuthenticationCodeFinal
398        //! \details The message authentication code is not keyed.
399        MessageAuthenticationCodeFinal() {}
400        //! \brief Construct a BlockCipherFinal
401        //! \param key a byte array used to key the algorithm
402        //! \details key must be at least DEFAULT_KEYLENGTH in length. Internally, the function calls
403        //!    SimpleKeyingInterface::SetKey.
404        MessageAuthenticationCodeFinal(const byte *key)
405                {this->SetKey(key, this->DEFAULT_KEYLENGTH);}
406        //! \brief Construct a BlockCipherFinal
407        //! \param key a byte array used to key the algorithm
408        //! \param length the length of the byte array
409        //! \details key must be at least DEFAULT_KEYLENGTH in length. Internally, the function calls
410        //!    SimpleKeyingInterface::SetKey.
411        MessageAuthenticationCodeFinal(const byte *key, size_t length)
412                {this->SetKey(key, length);}
413};
414
415// ************** documentation ***************
416
417//! \class BlockCipherDocumentation
418//! \brief Provides Encryption and Decryption typedefs used by derived classes to
419//!    implement a block cipher
420//! \details These objects usually should not be used directly. See CipherModeDocumentation
421//!    instead. Each class derived from this one defines two types, Encryption and Decryption,
422//!    both of which implement the BlockCipher interface.
423struct BlockCipherDocumentation
424{
425        //! implements the BlockCipher interface
426        typedef BlockCipher Encryption;
427        //! implements the BlockCipher interface
428        typedef BlockCipher Decryption;
429};
430
431//! \class SymmetricCipherDocumentation
432//! \brief Provides Encryption and Decryption typedefs used by derived classes to
433//!    implement a symmetric cipher
434//! \details Each class derived from this one defines two types, Encryption and Decryption,
435//!    both of which implement the SymmetricCipher interface. Two types of classes derive
436//!    from this class: stream ciphers and block cipher modes. Stream ciphers can be used
437//!    alone, cipher mode classes need to be used with a block cipher. See CipherModeDocumentation
438//!    for more for information about using cipher modes and block ciphers.
439struct SymmetricCipherDocumentation
440{
441        //! implements the SymmetricCipher interface
442        typedef SymmetricCipher Encryption;
443        //! implements the SymmetricCipher interface
444        typedef SymmetricCipher Decryption;
445};
446
447//! \class AuthenticatedSymmetricCipherDocumentation
448//! \brief Provides Encryption and Decryption typedefs used by derived classes to
449//!    implement an authenticated encryption cipher
450//! \details Each class derived from this one defines two types, Encryption and Decryption,
451//!    both of which implement the AuthenticatedSymmetricCipher interface.
452struct AuthenticatedSymmetricCipherDocumentation
453{
454        //! implements the AuthenticatedSymmetricCipher interface
455        typedef AuthenticatedSymmetricCipher Encryption;
456        //! implements the AuthenticatedSymmetricCipher interface
457        typedef AuthenticatedSymmetricCipher Decryption;
458};
459
460NAMESPACE_END
461
462#if CRYPTOPP_MSC_VERSION
463# pragma warning(pop)
464#endif
465
466#endif
Note: See TracBrowser for help on using the repository browser.