aboutsummaryrefslogtreecommitdiffstats
path: root/src/cli/cc_enc.cpp
blob: 25c9b960b0069877ae1ebe79c49027edc6ceb5c5 (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
/*
* (C) 2014,2015 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include "cli.h"
#include <botan/hex.h>

#if defined(BOTAN_HAS_FPE_FE1) && defined(BOTAN_HAS_PBKDF)

#include <botan/fpe_fe1.h>
#include <botan/pbkdf.h>

namespace Botan_CLI {

namespace {

uint8_t luhn_checksum(uint64_t cc_number)
   {
   uint8_t sum = 0;

   bool alt = false;
   while(cc_number)
      {
      uint8_t digit = cc_number % 10;
      if(alt)
         {
         digit *= 2;
         if(digit > 9)
            digit -= 9;
         }

      sum += digit;

      cc_number /= 10;
      alt = !alt;
      }

   return (sum % 10);
   }

bool luhn_check(uint64_t cc_number)
   {
   return (luhn_checksum(cc_number) == 0);
   }

uint64_t cc_rank(uint64_t cc_number)
   {
   // Remove Luhn checksum
   return cc_number / 10;
   }

uint64_t cc_derank(uint64_t cc_number)
   {
   for(size_t i = 0; i != 10; ++i)
      {
      if(luhn_check(cc_number * 10 + i))
         {
         return (cc_number * 10 + i);
         }
      }

   return 0;
   }

uint64_t encrypt_cc_number(uint64_t cc_number,
                           const Botan::secure_vector<uint8_t>& key,
                           const std::vector<uint8_t>& tweak)
   {
   const Botan::BigInt n = 1000000000000000;

   const uint64_t cc_ranked = cc_rank(cc_number);

   const Botan::BigInt c = Botan::FPE::fe1_encrypt(n, cc_ranked, key, tweak);

   if(c.bits() > 50)
      throw std::runtime_error("FPE produced a number too large");

   uint64_t enc_cc = 0;
   for(size_t i = 0; i != 7; ++i)
      enc_cc = (enc_cc << 8) | c.byte_at(6-i);
   return cc_derank(enc_cc);
   }

uint64_t decrypt_cc_number(uint64_t enc_cc,
                           const Botan::secure_vector<uint8_t>& key,
                           const std::vector<uint8_t>& tweak)
   {
   const Botan::BigInt n = 1000000000000000;

   const uint64_t cc_ranked = cc_rank(enc_cc);

   const Botan::BigInt c = Botan::FPE::fe1_decrypt(n, cc_ranked, key, tweak);

   if(c.bits() > 50)
      throw CLI_Error("FPE produced a number too large");

   uint64_t dec_cc = 0;
   for(size_t i = 0; i != 7; ++i)
      dec_cc = (dec_cc << 8) | c.byte_at(6-i);
   return cc_derank(dec_cc);
   }

}

class CC_Encrypt : public Command
   {
   public:
      CC_Encrypt() : Command("cc_encrypt CC passphrase --tweak=") {}

      void go()
         {
         const uint64_t cc_number = std::stoull(get_arg("CC"));
         const std::vector<uint8_t> tweak = Botan::hex_decode(get_arg("tweak"));
         const std::string pass = get_arg("passphrase");

         std::unique_ptr<Botan::PBKDF> pbkdf(Botan::PBKDF::create("PBKDF2(SHA-256)"));
         if(!pbkdf)
            throw CLI_Error_Unsupported("PBKDF", "PBKDF2(SHA-256)");

         Botan::secure_vector<uint8_t> key =
            pbkdf->pbkdf_iterations(32, pass,
                                    tweak.data(), tweak.size(),
                                    100000);

         output() << encrypt_cc_number(cc_number, key, tweak) << "\n";
         }
   };

BOTAN_REGISTER_COMMAND(CC_Encrypt);

class CC_Decrypt : public Command
   {
   public:
      CC_Decrypt() : Command("cc_decrypt CC passphrase --tweak=") {}

      void go() override
         {
         const uint64_t cc_number = std::stoull(get_arg("CC"));
         const std::vector<uint8_t> tweak = Botan::hex_decode(get_arg("tweak"));
         const std::string pass = get_arg("passphrase");

         std::unique_ptr<Botan::PBKDF> pbkdf(Botan::PBKDF::create("PBKDF2(SHA-256)"));
         if(!pbkdf)
            throw CLI_Error_Unsupported("PBKDF", "PBKDF2(SHA-256)");

         Botan::secure_vector<uint8_t> key =
            pbkdf->pbkdf_iterations(32, pass,
                                    tweak.data(), tweak.size(),
                                    100000);

         output() << decrypt_cc_number(cc_number, key, tweak) << "\n";
         }
   };

BOTAN_REGISTER_COMMAND(CC_Decrypt);

}

#endif // FPE && PBKDF