ledger-core
zarith.h
1 /*
2  *
3  * zarith
4  *
5  * Created by El Khalil Bellakrid on 08/05/2019.
6  *
7  * The MIT License (MIT)
8  *
9  * Copyright (c) 2019 Ledger
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a copy
12  * of this software and associated documentation files (the "Software"), to deal
13  * in the Software without restriction, including without limitation the rights
14  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15  * copies of the Software, and to permit persons to whom the Software is
16  * furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included in all
19  * copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27  * SOFTWARE.
28  *
29  */
30 
31 
32 #ifndef LEDGER_CORE_ZARITH_H
33 #define LEDGER_CORE_ZARITH_H
34 
35 #include <algorithm>
36 #include <vector>
37 #include <bytes/BytesReader.h>
38 #include <utils/hex.h>
39 
40 namespace ledger {
41  namespace core {
42  class zarith {
43  public:
44  /*
45  * Most significant bit of each byte is replaced with 1 to let
46  * us know there is still something to read, else 0
47  * To sum up, if the resulting bytes are "chained" together (little endian) and we drop
48  * the most significant bit of each byte, we get the input ("chained") bytes
49  * e.g.
50  * Input: ad d9 10101101 11011001 (I)
51  * Output: d9 db 02 11011001 11011011 00000010
52  * LE: 02 db d9 00000010 11011011 11011001
53  * Drop 1st bit: (0)0000010 (1)1011011 (1)1011001 == (I)
54  */
55  static std::vector<uint8_t> zSerializeNumber(const std::vector<uint8_t> &inputData) {
56  std::vector<uint8_t> result;
57  size_t id = inputData.size() - 1;
58  uint32_t offset = 0;
59  bool needAdditionalByte = false;
60  while (id >= 0 && id < inputData.size()) {
61  auto byte = inputData[id];
62 
63  // We shift current byte
64  byte <<= offset;
65 
66  // Take shifted bits from previous byte
67  byte |= id < inputData.size() - 1 ? inputData[id + 1] >> (7 - (offset - 1)) : 0x00;
68 
69  // This boolean will let us know if shifting last byte of inputData
70  // will generate a new byte (with remaining shifted bytes)
71  bool isFirst = id == 0;
72  needAdditionalByte = isFirst && inputData[id] >> (7 - offset);
73 
74  // We set first bit to 1 to know that there is a coming byte
75  byte |= 0x80;
76 
77  // Push to result modified byte
78  result.push_back(byte);
79 
80  // Update offset
81  offset = (offset + 1) % 8;
82 
83  // Update index if not a period or first iteration
84  if (offset != 0) {
85  id --;
86  }
87  }
88 
89  if (needAdditionalByte) {
90  if (offset != 0) {
91  id ++;
92  }
93  offset = (offset - 1) % 8;
94  result.push_back(inputData[id] >> (7 - offset));
95  } else {
96  result[result.size() - 1] &= 0x7F;
97  }
98 
99 
100  return result;
101  };
102 
103  static std::vector<uint8_t> zParse(const std::vector<uint8_t> &inputData) {
104  // Reverse because result corresponds to Little Endian
105  BytesReader bytesReader(inputData);
106  return zParse(bytesReader);
107  };
108 
109  // The mode where we force parser to continue parsing even when finding
110  // a null bit was made just for testing purpose
111  static std::vector<uint8_t> zParse(BytesReader &reader) {
112  // We get bytes to parse without altering
113  // bytes reader's state
114  auto cursor = reader.getCursor();
115  std::vector<uint8_t> data(reader.readUntilEnd());
116  reader.reset();
117  reader.read(cursor);
118 
119  std::vector<uint8_t> result;
120  size_t id = 0, delay = 0;
121  uint8_t offset = 0;
122  while (id + delay < data.size()) {
123  auto byte = data[id + delay];
124  auto isLast = id + delay == data.size() - 1;
125 
126  // We should stop reading if most significant bit is null
127  auto shouldStop = !(byte & 0x80);
128 
129  // Mask last bit, which is here just to let us
130  // know if there is more data to read
131  byte &= data[id + delay] & 0x7F;
132 
133  // Shift to retrieve only bits of original byte
134  byte >>= offset;
135 
136  // Get shifted bits that has been moved to next byte
137  byte |= !isLast && !shouldStop ? data[id + delay + 1] << (7 - offset) : 0x00;
138 
139  if (!(isLast && byte == 0)) { // This condition is here to prevent having unnecessary leading null byte
140  result.push_back(byte);
141  }
142 
143  // Update
144  id++;
145  delay = id / 7;
146  offset++;
147  offset %= 7;
148 
149  if (shouldStop) {
150  break;
151  }
152  }
153 
154  // Update bytes reader
155  auto min = std::min<unsigned long>(id + delay, data.size());
156  reader.read(min);
157 
158  // Little endianess
159  std::reverse(result.begin(), result.end());
160 
161  return result;
162  };
163  };
164  }
165 }
166 #endif //LEDGER_CORE_ZARITH_H
std::vector< uint8_t > read(unsigned long length)
Definition: BytesReader.cpp:71
unsigned long getCursor() const
Definition: BytesReader.cpp:80
Definition: zarith.h:42
Definition: BytesReader.h:47
void reset()
Definition: BytesReader.cpp:76
Definition: Account.cpp:8