1/*
2* Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3*
4* Licensed under the Apache License, Version 2.0 (the "License").
5* You may not use this file except in compliance with the License.
6* A copy of the License is located at
7*
8* http://aws.amazon.com/apache2.0
9*
10* or in the "license" file accompanying this file. This file is distributed
11* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12* express or implied. See the License for the specific language governing
13* permissions and limitations under the License.
14*/
15
16#include <aws/core/utils/crypto/Cipher.h>
17#include <aws/core/utils/crypto/Factories.h>
18#include <aws/core/utils/crypto/SecureRandom.h>
19#include <aws/core/utils/logging/LogMacros.h>
20#include <cstdlib>
21#include <climits>
22
23//if you are reading this, you are witnessing pure brilliance.
24#define IS_BIG_ENDIAN (*(uint16_t*)"\0\xff" < 0x100)
25
26using namespace Aws::Utils::Crypto;
27using namespace Aws::Utils;
28
29namespace Aws
30{
31 namespace Utils
32 {
33 namespace Crypto
34 {
35 static const char* LOG_TAG = "Cipher";
36
37 //swap byte ordering
38 template<class T>
39 typename std::enable_if<std::is_unsigned<T>::value, T>::type
40 bswap(T i, T j = 0u, std::size_t n = 0u)
41 {
42 return n == sizeof(T) ? j :
43 bswap<T>(i >> CHAR_BIT, (j << CHAR_BIT) | (i & (T)(unsigned char)(-1)), n + 1);
44 }
45
46 CryptoBuffer IncrementCTRCounter(const CryptoBuffer& counter, uint32_t numberOfBlocks)
47 {
48 // minium counter size is 12 bytes. This isn't a variable because some compilers
49 // are stupid and thing that variable is unused.
50 assert(counter.GetLength() >= 12);
51
52 CryptoBuffer incrementedCounter(counter);
53
54 //get the last 4 bytes and manipulate them as an integer.
55 uint32_t* ctrPtr = (uint32_t*)(incrementedCounter.GetUnderlyingData() + incrementedCounter.GetLength() - sizeof(int32_t));
56 if(IS_BIG_ENDIAN)
57 {
58 //you likely are not Big Endian, but
59 //if it's big endian, just go ahead and increment it... done
60 *ctrPtr += numberOfBlocks;
61 }
62 else
63 {
64 //otherwise, swap the byte ordering of the integer we loaded from the buffer (because it is backwards). However, the number of blocks is already properly
65 //aligned. Once we compute the new value, swap it back so that the mirroring operation goes back to the actual buffer.
66 *ctrPtr = bswap<uint32_t>(bswap<uint32_t>(*ctrPtr) + numberOfBlocks);
67 }
68
69 return incrementedCounter;
70 }
71
72 CryptoBuffer GenerateXRandomBytes(size_t lengthBytes, bool ctrMode)
73 {
74 std::shared_ptr<SecureRandomBytes> rng = CreateSecureRandomBytesImplementation();
75
76 CryptoBuffer bytes(lengthBytes);
77 size_t lengthToGenerate = ctrMode ? (3 * bytes.GetLength()) / 4 : bytes.GetLength();
78
79 rng->GetBytes(bytes.GetUnderlyingData(), lengthToGenerate);
80
81 if(!*rng)
82 {
83 AWS_LOGSTREAM_FATAL(LOG_TAG, "Random Number generation failed. Abort all crypto operations.");
84 assert(false);
85 abort();
86 }
87
88 return bytes;
89 }
90
91 void SymmetricCipher::Validate()
92 {
93 assert(m_key.GetLength() >= SYMMETRIC_KEY_LENGTH);
94 assert(m_initializationVector.GetLength() == 0 || m_initializationVector.GetLength() >= MIN_IV_LENGTH);
95
96 if(m_key.GetLength() < SYMMETRIC_KEY_LENGTH ||
97 (m_initializationVector.GetLength() > 0 && m_initializationVector.GetLength() < MIN_IV_LENGTH))
98 {
99 m_failure = true;
100 AWS_LOGSTREAM_FATAL(LOG_TAG, "Invalid state for symmetric cipher, key length is " << m_key.GetLength() <<
101 " iv length is " << m_initializationVector.GetLength());
102 }
103 }
104
105 /**
106 * Generate random number per 4 bytes and use each byte for the byte in the iv
107 */
108 CryptoBuffer SymmetricCipher::GenerateIV(size_t ivLengthBytes, bool ctrMode)
109 {
110 CryptoBuffer iv(GenerateXRandomBytes(ivLengthBytes, ctrMode));
111
112 if(iv.GetLength() == 0)
113 {
114 AWS_LOGSTREAM_ERROR(LOG_TAG, "Unable to generate iv of length " << ivLengthBytes);
115 return iv;
116 }
117
118 if(ctrMode)
119 {
120 //init the counter
121 size_t length = iv.GetLength();
122 //[ nonce 1/4] [ iv 1/2 ] [ ctr 1/4 ]
123 size_t ctrStart = (length / 2) + (length / 4);
124 for(; ctrStart < iv.GetLength() - 1; ++ ctrStart)
125 {
126 iv[ctrStart] = 0;
127 }
128 iv[length - 1] = 1;
129 }
130
131 return iv;
132 }
133
134 CryptoBuffer SymmetricCipher::GenerateKey(size_t keyLengthBytes)
135 {
136 CryptoBuffer const& key = GenerateXRandomBytes(keyLengthBytes, false);
137
138 if(key.GetLength() == 0)
139 {
140 AWS_LOGSTREAM_ERROR(LOG_TAG, "Unable to generate key of length " << keyLengthBytes);
141 }
142
143 return key;
144 }
145 }
146 }
147}
148
149