aboutsummaryrefslogtreecommitdiffstats
path: root/src/cms/cms_algo.cpp
blob: fadc68da5d45e6a199c80cbaa1993235ad4ff217 (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
/*************************************************
* CMS Algorithm Specific Code Source File        *
* (C) 1999-2007 Jack Lloyd                       *
*************************************************/

#include <botan/cms_enc.h>
#include <botan/der_enc.h>
#include <botan/lookup.h>
#include <botan/filters.h>
#include <botan/libstate.h>
#include <botan/rc2.h>

namespace Botan {

namespace {

/*************************************************
* Wrap a key as specified in RFC 3217            *
*************************************************/
SecureVector<byte> do_rfc3217_wrap(RandomNumberGenerator& rng,
                                   const std::string& cipher,
                                   const SymmetricKey& kek,
                                   const SecureVector<byte>& input)
   {
   class Flip_Bytes : public Filter
      {
      public:
         void write(const byte data[], u32bit length)
            {
            buf.append(data, length);
            }
         void end_msg()
            {
            for(u32bit j = 0; j != buf.size(); j++)
               send(buf[buf.size()-j-1]);
            buf.destroy();
            }
         Flip_Bytes(const SecureVector<byte>& prefix) { buf.append(prefix); }
      private:
         SecureVector<byte> buf;
      };

   if(block_size_of(cipher) != 8)
      throw Encoding_Error("do_rfc3217_wrap: Bad cipher: " + cipher);

   Pipe icv(new Hash_Filter("SHA-160", 8));
   icv.process_msg(input);

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

   Pipe pipe(get_cipher(global_state(),
                        cipher + "/CBC/NoPadding", kek, iv, ENCRYPTION),
             new Flip_Bytes(iv.bits_of()),
             get_cipher(global_state(),
                        cipher + "/CBC/NoPadding", kek, fixed, ENCRYPTION));
   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(cipher == "TripleDES")
      {
      SymmetricKey cek_parity = cek;
      cek_parity.set_odd_parity();
      return do_rfc3217_wrap(rng, cipher, kek, cek_parity.bits_of());
      }
   else 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.append((byte)cek.length());
      lcekpad.append(cek.bits_of());
      while(lcekpad.size() % 8)
         lcekpad.append(rng.next_byte());
      return do_rfc3217_wrap(rng, cipher, kek, lcekpad);
      }
   else
      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(cipher == "RC2")
      {
      encoder.start_cons(SEQUENCE).
         encode((u32bit)RC2::EKB_code(8*key.length())).
         encode(iv.bits_of(), OCTET_STRING).
      end_cons();
      }
   else 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)
   {
   u32bit 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;
   }

}