aboutsummaryrefslogtreecommitdiffstats
path: root/src/cms/cms_algo.cpp
blob: 3c245cc6f780e48bd2c0185b57cfbba43b398667 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/*
* CMS Algorithm Specific Code
* (C) 1999-2007 Jack Lloyd
*
* Distributed under the terms of the Botan license
*/

#include <botan/cms_enc.h>
#include <botan/der_enc.h>
#include <botan/sha160.h>
#include <botan/cbc.h>
#include <botan/filters.h>
#include <botan/libstate.h>

#if defined(BOTAN_HAS_RC2)
  #include <botan/rc2.h>
#endif

namespace Botan {

namespace {

/*
* Wrap a key as specified in RFC 3217
*/
SecureVector<byte> do_rfc3217_wrap(RandomNumberGenerator& rng,
                                   const std::string& cipher_name,
                                   const SymmetricKey& kek,
                                   const SecureVector<byte>& input)
   {
   class Flip_Bytes : public Filter
      {
      public:
         std::string name() const { return "Fip_Bytes"; }

         void write(const byte data[], size_t length)
            {
            buf += std::make_pair(data, length);
            }
         void end_msg()
            {
            for(size_t j = 0; j != buf.size(); j++)
               send(buf[buf.size()-j-1]);
            buf.clear();
            }

         Flip_Bytes(const SecureVector<byte>& prefix) : buf(prefix) {}
      private:
         SecureVector<byte> buf;
      };

   Algorithm_Factory& af = global_state().algorithm_factory();

   const BlockCipher* cipher = af.prototype_block_cipher(cipher_name);

   if(!cipher || cipher->block_size() != 8)
      throw Encoding_Error("do_rfc3217_wrap: Bad cipher: " + cipher_name);

   Pipe icv(new Hash_Filter(new SHA_160, 8));
   icv.process_msg(input);

   InitializationVector iv(rng, 8);
   InitializationVector fixed("4ADDA22C79E82105");

   Pipe pipe(new CBC_Encryption(cipher->clone(), new Null_Padding, kek, iv),
             new Flip_Bytes(iv.bits_of()),
             new CBC_Encryption(cipher->clone(), new Null_Padding, kek, iv));

   pipe.start_msg();
   pipe.write(input);
   pipe.write(icv.read_all());
   pipe.end_msg();
   return pipe.read_all();
   }

}

/*
* Wrap a CEK with a KEK
*/
SecureVector<byte> CMS_Encoder::wrap_key(RandomNumberGenerator& rng,
                                         const std::string& cipher,
                                         const SymmetricKey& cek,
                                         const SymmetricKey& kek)
   {
#if defined(BOTAN_HAS_DES)
   if(cipher == "TripleDES")
      {
      SymmetricKey cek_parity = cek;
      cek_parity.set_odd_parity();
      return do_rfc3217_wrap(rng, cipher, kek, cek_parity.bits_of());
      }
#endif

#if defined(BOTAN_HAS_RC2) || defined(BOTAN_HAS_CAST)
   if(cipher == "RC2" || cipher == "CAST-128")
      {
      if(kek.length() != 16)
         throw Encoding_Error("CMS: 128-bit KEKs must be used with " + cipher);

      SecureVector<byte> lcekpad;
      lcekpad.push_back(static_cast<byte>(cek.length()));
      lcekpad += cek.bits_of();
      while(lcekpad.size() % 8)
         lcekpad.push_back(rng.next_byte());
      return do_rfc3217_wrap(rng, cipher, kek, lcekpad);
      }
#endif

   throw Invalid_Argument("CMS_Encoder::wrap: Unknown cipher " + cipher);
   }

/*
* Encode the parameters for an encryption algo
*/
SecureVector<byte> CMS_Encoder::encode_params(const std::string& cipher,
                                              const SymmetricKey& key,
                                              const InitializationVector& iv)
   {
   DER_Encoder encoder;

#if defined(BOTAN_HAS_RC2)
   if(cipher == "RC2")
      {
      encoder.start_cons(SEQUENCE).
         encode(static_cast<size_t>(RC2::EKB_code(8*key.length()))).
         encode(iv.bits_of(), OCTET_STRING).
      end_cons();
      return encoder.get_contents();
      }
#endif

   if(cipher == "CAST-128")
      {
      encoder.start_cons(SEQUENCE).
         encode(iv.bits_of(), OCTET_STRING).
         encode(8*key.length()).
      end_cons();
      }
   else
      encoder.encode(iv.bits_of(), OCTET_STRING);

   return encoder.get_contents();
   }

/*
* Generate a CEK or KEK for the cipher
*/
SymmetricKey CMS_Encoder::setup_key(RandomNumberGenerator& rng,
                                    const std::string& cipher)
   {
   size_t keysize = 0;

   if(cipher == "TripleDES") keysize = 24;
   if(cipher == "RC2")       keysize = 16;
   if(cipher == "CAST-128")  keysize = 16;

   if(keysize == 0)
      throw Invalid_Argument("CMS: Cannot encrypt with cipher " + cipher);

   SymmetricKey key(rng, keysize);
   if(cipher == "DES" || cipher == "TripleDES")
      key.set_odd_parity();
   return key;
   }

}