1package tg 2 3import ( 4 crand "crypto/rand" 5 "fmt" 6 "math/rand" 7 8 "github.com/ooni/psiphon/oopsi/github.com/redjack/marionette" 9 "github.com/ooni/psiphon/oopsi/github.com/redjack/marionette/fte" 10) 11 12type AmazonMsgLensCipher struct { 13 key string 14 min int 15 max int 16 target int 17 regex string 18} 19 20func NewAmazonMsgLensCipher(key, regex string) *AmazonMsgLensCipher { 21 return &AmazonMsgLensCipher{ 22 key: key, 23 min: fte.COVERTEXT_HEADER_LEN_CIPHERTTEXT + fte.CTXT_EXPANSION + 32, 24 max: 1 << 18, 25 target: 0, 26 regex: regex, 27 } 28} 29 30func (h *AmazonMsgLensCipher) Key() string { return h.key } 31 32func (h *AmazonMsgLensCipher) Capacity(fsm marionette.FSM) (int, error) { 33 h.target = amazonMsgLens[rand.Intn(len(amazonMsgLens))] 34 if h.target < h.min { 35 return 0, nil 36 } else if h.target > h.max { 37 // We do this to prevent unranking really large slices 38 // in practice this is probably bad since it unnaturally caps 39 // our message sizes to whatever FTE can support 40 h.target = h.max 41 return h.max, nil 42 } 43 n := h.target - fte.COVERTEXT_HEADER_LEN_CIPHERTTEXT 44 n -= fte.CTXT_EXPANSION 45 n -= 1 46 return n, nil 47} 48 49func (h *AmazonMsgLensCipher) Encrypt(fsm marionette.FSM, template string, plaintext []byte) (ciphertext []byte, err error) { 50 if h.target < h.min || h.target > h.max { 51 dfa, err := fsm.DFA(h.regex, h.target) 52 if err != nil { 53 return nil, err 54 } 55 56 numWords, err := dfa.NumWordsInSlice(h.target) 57 if err != nil { 58 return nil, err 59 } 60 61 rnd, err := crand.Int(crand.Reader, numWords) 62 if err != nil { 63 return nil, err 64 } 65 66 ret, err := dfa.Unrank(rnd) 67 if err != nil { 68 return nil, err 69 } 70 return []byte(ret), nil 71 } 72 73 cipher, err := fsm.Cipher(h.regex, h.min) 74 if err != nil { 75 return nil, err 76 } 77 78 ciphertext, err = cipher.Encrypt(plaintext) 79 if err != nil { 80 return nil, err 81 } else if len(ciphertext) != h.target { 82 return nil, fmt.Errorf("Could not find ciphertext of len %d (%d)", h.target, len(ciphertext)) 83 } 84 return ciphertext, nil 85} 86 87func (h *AmazonMsgLensCipher) Decrypt(fsm marionette.FSM, ciphertext []byte) (plaintext []byte, err error) { 88 if len(ciphertext) < h.min { 89 return nil, nil 90 } 91 cipher, err := fsm.Cipher(h.regex, h.min) 92 if err != nil { 93 return nil, err 94 } 95 plaintext, _, err = cipher.Decrypt(ciphertext) 96 return plaintext, err 97} 98 99// This a weighted list of message lengths. 100var amazonMsgLens []int 101 102func init() { 103 for _, item := range []struct { 104 n int 105 weight int 106 }{ 107 {n: 2049, weight: 1}, 108 {n: 2052, weight: 2}, 109 {n: 2054, weight: 2}, 110 {n: 2057, weight: 3}, 111 {n: 2058, weight: 2}, 112 {n: 2059, weight: 1}, 113 {n: 2065, weight: 1}, 114 {n: 17429, weight: 1}, 115 {n: 3098, weight: 1}, 116 {n: 687, weight: 3}, 117 {n: 2084, weight: 1}, 118 {n: 42, weight: 58}, 119 {n: 43, weight: 107}, 120 {n: 9260, weight: 1}, 121 {n: 11309, weight: 1}, 122 {n: 11829, weight: 1}, 123 {n: 9271, weight: 1}, 124 {n: 6154, weight: 1}, 125 {n: 64, weight: 15}, 126 {n: 1094, weight: 1}, 127 {n: 12376, weight: 1}, 128 {n: 89, weight: 1}, 129 {n: 10848, weight: 1}, 130 {n: 5223, weight: 1}, 131 {n: 69231, weight: 1}, 132 {n: 7795, weight: 1}, 133 {n: 2678, weight: 1}, 134 {n: 8830, weight: 1}, 135 {n: 29826, weight: 1}, 136 {n: 16006, weight: 10}, 137 {n: 8938, weight: 1}, 138 {n: 17055, weight: 2}, 139 {n: 87712, weight: 1}, 140 {n: 23202, weight: 1}, 141 {n: 7441, weight: 1}, 142 {n: 17681, weight: 1}, 143 {n: 12456, weight: 1}, 144 {n: 41132, weight: 1}, 145 {n: 25263, weight: 6}, 146 {n: 689, weight: 1}, 147 {n: 9916, weight: 1}, 148 {n: 10101, weight: 2}, 149 {n: 1730, weight: 1}, 150 {n: 10948, weight: 1}, 151 {n: 26826, weight: 1}, 152 {n: 6357, weight: 1}, 153 {n: 13021, weight: 2}, 154 {n: 1246, weight: 4}, 155 {n: 19683, weight: 1}, 156 {n: 1765, weight: 1}, 157 {n: 1767, weight: 1}, 158 {n: 1768, weight: 1}, 159 {n: 1769, weight: 4}, 160 {n: 1770, weight: 6}, 161 {n: 1771, weight: 3}, 162 {n: 1772, weight: 2}, 163 {n: 1773, weight: 4}, 164 {n: 1774, weight: 4}, 165 {n: 1775, weight: 1}, 166 {n: 1776, weight: 1}, 167 {n: 1779, weight: 1}, 168 {n: 40696, weight: 1}, 169 {n: 767, weight: 1}, 170 {n: 17665, weight: 1}, 171 {n: 27909, weight: 1}, 172 {n: 12550, weight: 1}, 173 {n: 5385, weight: 1}, 174 {n: 16651, weight: 1}, 175 {n: 5392, weight: 1}, 176 {n: 26385, weight: 1}, 177 {n: 12056, weight: 1}, 178 {n: 41245, weight: 2}, 179 {n: 13097, weight: 1}, 180 {n: 15152, weight: 1}, 181 {n: 310, weight: 1}, 182 {n: 40759, weight: 1}, 183 {n: 9528, weight: 1}, 184 {n: 8000, weight: 7}, 185 {n: 471, weight: 1}, 186 {n: 15180, weight: 1}, 187 {n: 14158, weight: 3}, 188 {n: 37719, weight: 2}, 189 {n: 1895, weight: 1}, 190 {n: 31082, weight: 1}, 191 {n: 19824, weight: 1}, 192 {n: 30956, weight: 1}, 193 {n: 18807, weight: 1}, 194 {n: 11095, weight: 1}, 195 {n: 37756, weight: 2}, 196 {n: 746, weight: 1}, 197 {n: 10475, weight: 1}, 198 {n: 4332, weight: 1}, 199 {n: 35730, weight: 1}, 200 {n: 11667, weight: 1}, 201 {n: 16788, weight: 1}, 202 {n: 12182, weight: 4}, 203 {n: 39663, weight: 1}, 204 {n: 9126, weight: 1}, 205 {n: 35760, weight: 1}, 206 {n: 12735, weight: 1}, 207 {n: 6594, weight: 1}, 208 {n: 451, weight: 15}, 209 {n: 19402, weight: 1}, 210 {n: 463, weight: 3}, 211 {n: 10193, weight: 1}, 212 {n: 16853, weight: 6}, 213 {n: 982, weight: 1}, 214 {n: 15865, weight: 1}, 215 {n: 2008, weight: 2}, 216 {n: 476, weight: 1}, 217 {n: 13655, weight: 1}, 218 {n: 10213, weight: 1}, 219 {n: 10737, weight: 1}, 220 {n: 15858, weight: 1}, 221 {n: 2035, weight: 6}, 222 {n: 2039, weight: 1}, 223 {n: 2041, weight: 2}, 224 } { 225 for i := 0; i < item.weight; i++ { 226 amazonMsgLens = append(amazonMsgLens, item.n) 227 } 228 } 229} 230