@@ -89,6 +89,24 @@ class EncodedAbyssAssets:
8989 opcodes : dict [str , int ]
9090
9191
92+ _JUMP_OPS = {
93+ "JUMP" ,
94+ "JUMP_IF_FALSE" ,
95+ "JUMP_IF_TRUE_KEEP" ,
96+ "JUMP_IF_FALSE_KEEP" ,
97+ "FOR_ITER" ,
98+ }
99+
100+ _PACKET_LAYOUTS = (
101+ (0 , 1 , 2 ),
102+ (0 , 2 , 1 ),
103+ (1 , 0 , 2 ),
104+ (1 , 2 , 0 ),
105+ (2 , 0 , 1 ),
106+ (2 , 1 , 0 ),
107+ )
108+
109+
92110class _Label :
93111 def __init__ (self ) -> None :
94112 self .index : int | None = None
@@ -237,7 +255,7 @@ def compile(self, node: ast.FunctionDef) -> dict[str, Any]:
237255 return {
238256 "name" : node .name ,
239257 "code" : self .emitter .resolve (),
240- "consts" : [ _encode_const ( value ) for value in self .constants ] ,
258+ "consts" : list ( self .constants ) ,
241259 "globals" : sorted (self .global_names ),
242260 "locals" : sorted (self .local_names ),
243261 "externals" : sorted (self .external_names ),
@@ -628,22 +646,78 @@ def _encode_const(value: Any) -> dict[str, Any]:
628646 raise UnsupportedAbyssNode (f"unsupported constant type { type (value ).__name__ } " )
629647
630648
649+ def _json_bytes (value : Any ) -> bytes :
650+ return json .dumps (value , separators = ("," , ":" ), sort_keys = True ).encode ("utf-8" )
651+
652+
653+ def _seal_value (value : Any , rng : random .Random , salt : int ) -> list [Any ]:
654+ raw = _json_bytes (value )
655+ key = gen_bytes (rng , rng .randint (9 , 24 ))
656+ add = rng .randrange (256 )
657+ step = rng .randrange (1 , 256 , 2 )
658+ encoded = bytes (((byte ^ key [index % len (key )]) + add + ((index + salt ) * step )) & 255 for index , byte in enumerate (raw ))
659+ share = gen_bytes (rng , len (encoded ))
660+ other = bytes (left ^ right for left , right in zip (encoded , share ))
661+ return [
662+ base64 .b85encode (share ).decode ("ascii" ),
663+ base64 .b85encode (other ).decode ("ascii" ),
664+ base64 .b85encode (key ).decode ("ascii" ),
665+ add ,
666+ step ,
667+ ]
668+
669+
670+ def _tag_for (opcode : int , argc : int , index : int , salt : int ) -> int :
671+ return ((opcode * 2654435761 ) ^ (argc * 2246822519 ) ^ ((index + salt ) * 3266489917 )) & 0xFFFFFFFF
672+
673+
674+ def _encode_packet (inst : list [Any ], index : int , rng : random .Random , salt : int ) -> list [Any ]:
675+ opcode = inst [0 ]
676+ args = inst [1 :]
677+ tag = _tag_for (opcode , len (args ), index , salt )
678+ fields = [
679+ _seal_value (opcode , rng , salt + index * 5 + 1 ),
680+ _seal_value (args , rng , salt + index * 5 + 2 ),
681+ _seal_value (tag , rng , salt + index * 5 + 3 ),
682+ ]
683+ layout_id = rng .randrange (len (_PACKET_LAYOUTS ))
684+ layout = _PACKET_LAYOUTS [layout_id ]
685+ return [layout_id , fields [layout [0 ]], fields [layout [1 ]], fields [layout [2 ]]]
686+
687+
688+ def _rebased_instruction (inst : list [Any ], base : int ) -> list [Any ]:
689+ op = inst [0 ]
690+ if op in _JUMP_OPS :
691+ return [op , base + inst [1 ], * inst [2 :]]
692+ return list (inst )
693+
694+
631695def encode_assets (assets : list [dict [str , Any ]], rng : random .Random ) -> EncodedAbyssAssets :
632696 values = rng .sample (range (1 , 251 ), len (_OPS ))
633697 opcodes = dict (zip (_OPS , values ))
634698 encoded_funcs : list [dict [str , Any ]] = []
635- for asset in assets :
636- encoded_code = [[opcodes [inst [0 ]], * inst [1 :]] for inst in asset ["code" ]]
699+ merged_code : list [list [Any ]] = []
700+ salt = rng .randrange (1 << 30 )
701+ for fn_index , asset in enumerate (assets ):
702+ entry = len (merged_code )
703+ rebased_code = [_rebased_instruction (inst , entry ) for inst in asset ["code" ]]
704+ merged_code .extend (rebased_code )
637705 encoded_funcs .append (
638706 {
639- "n" : asset ["name" ],
640- "c" : asset ["consts" ],
641- "g" : asset ["globals" ],
642- "l" : asset ["locals" ],
643- "b" : encoded_code ,
707+ "c" : [
708+ _seal_value (_encode_const (value ), rng , salt + (fn_index + 1 ) * 100000 + const_index )
709+ for const_index , value in enumerate (asset ["consts" ])
710+ ],
711+ "e" : _seal_value (entry , rng , salt + 200000 + fn_index ),
712+ "g" : _seal_value (asset ["globals" ], rng , salt + 300000 + fn_index ),
713+ "l" : _seal_value (asset ["locals" ], rng , salt + 400000 + fn_index ),
644714 }
645715 )
646- document = json .dumps ({"v" : 1 , "f" : encoded_funcs }, separators = ("," , ":" ), sort_keys = True ).encode ("utf-8" )
716+ encoded_packets = [
717+ _encode_packet ([opcodes [inst [0 ]], * inst [1 :]], index , rng , salt )
718+ for index , inst in enumerate (merged_code )
719+ ]
720+ document = _json_bytes ({"v" : 2 , "m" : {"s" : salt }, "f" : encoded_funcs , "p" : encoded_packets })
647721 key = gen_bytes (rng , rng .randint (24 , 48 ))
648722 encrypted = bytes (byte ^ key [index % len (key )] for index , byte in enumerate (document ))
649723 return EncodedAbyssAssets (
@@ -657,10 +731,42 @@ def build_runtime_stmts(encoded: EncodedAbyssAssets, rng: random.Random) -> list
657731 blocks = _runtime_dispatch_blocks (encoded .opcodes )
658732 rng .shuffle (blocks )
659733 dispatch_blocks = "\n " .join (blocks )
734+ layouts_src = repr (_PACKET_LAYOUTS )
660735 source = f'''
661736{ ASSETS_NAME } = ({ encoded .payload !r} , { encoded .key !r} )
662737__pw_ab_cache__ = None
663738
739+ def __pw_ab_open__(_box, _salt):
740+ _base64 = __import__('base64')
741+ _json = __import__('json')
742+ _share = _base64.b85decode(_box[0].encode('ascii'))
743+ _other = _base64.b85decode(_box[1].encode('ascii'))
744+ _key = _base64.b85decode(_box[2].encode('ascii'))
745+ _add = _box[3]
746+ _step = _box[4]
747+ _encoded = bytes(_a ^ _b for _a, _b in zip(_share, _other))
748+ _raw = bytearray(len(_encoded))
749+ for _i, _b in enumerate(_encoded):
750+ _raw[_i] = ((_b - _add - ((_i + _salt) * _step)) & 255) ^ _key[_i % len(_key)]
751+ return _json.loads(bytes(_raw).decode('utf-8'))
752+
753+ def __pw_ab_tag__(_opcode, _argc, _index, _salt):
754+ return ((_opcode * 2654435761) ^ (_argc * 2246822519) ^ ((_index + _salt) * 3266489917)) & 4294967295
755+
756+ def __pw_ab_packet__(_packet, _index, _meta):
757+ _layouts = { layouts_src }
758+ _layout = _layouts[_packet[0] % len(_layouts)]
759+ _fields = [None, None, None]
760+ for _pos, _slot in enumerate(_layout):
761+ _fields[_slot] = _packet[_pos + 1]
762+ _salt = _meta['s']
763+ _opcode = __pw_ab_open__(_fields[0], _salt + _index * 5 + 1)
764+ _args = __pw_ab_open__(_fields[1], _salt + _index * 5 + 2)
765+ _tag = __pw_ab_open__(_fields[2], _salt + _index * 5 + 3)
766+ if _tag != __pw_ab_tag__(_opcode, len(_args), _index, _salt):
767+ raise RuntimeError('invalid abyss packet')
768+ return [_opcode, *_args]
769+
664770def __pw_ab_const__(_x):
665771 _t = _x['t']
666772 if _t == 'none':
@@ -689,8 +795,12 @@ def __pw_ab_load__():
689795 _raw_key = _base64.b85decode(_key.encode('ascii'))
690796 _raw = bytes(_b ^ _raw_key[_i % len(_raw_key)] for _i, _b in enumerate(_enc))
691797 _doc = _json.loads(_raw.decode('utf-8'))
692- for _fn in _doc['f']:
693- _fn['c'] = [__pw_ab_const__(_item) for _item in _fn['c']]
798+ _meta = _doc['m']
799+ for _fi, _fn in enumerate(_doc['f']):
800+ _fn['c'] = [__pw_ab_const__(__pw_ab_open__(_item, _meta['s'] + (_fi + 1) * 100000 + _ci)) for _ci, _item in enumerate(_fn['c'])]
801+ _fn['e'] = __pw_ab_open__(_fn['e'], _meta['s'] + 200000 + _fi)
802+ _fn['g'] = __pw_ab_open__(_fn['g'], _meta['s'] + 300000 + _fi)
803+ _fn['l'] = __pw_ab_open__(_fn['l'], _meta['s'] + 400000 + _fi)
694804 __pw_ab_cache__ = _doc
695805 return __pw_ab_cache__
696806
@@ -790,26 +900,27 @@ def __pw_ab_format__(_value, _conversion, _has_spec, _stack):
790900 _value = ascii(_value)
791901 return format(_value, _spec)
792902
793- def __pw_ab_exec__(_fn, _initial_locals, _globals):
903+ def __pw_ab_exec__(_doc, _fn, _initial_locals, _globals):
794904 _builtins = _globals.get('__builtins__', __builtins__)
795905 if not isinstance(_builtins, dict):
796906 _builtins = _builtins.__dict__
797907 _declared_globals = set(_fn.get('g', ()))
798908 _declared_locals = set(_fn.get('l', ()))
799909 _locals = dict(_initial_locals)
800910 _consts = _fn['c']
801- _code = _fn['b']
911+ _code = _doc['p']
912+ _meta = _doc['m']
802913 _stack = []
803- _ip = 0
914+ _ip = _fn['e']
804915 while True:
805- _inst = _code[_ip]
916+ _inst = __pw_ab_packet__( _code[_ip], _ip, _meta)
806917 _op = _inst[0]
807918{ dispatch_blocks }
808919 raise RuntimeError('invalid abyss opcode')
809920
810921def { DISPATCH_NAME } (_fid, _env):
811922 _doc = __pw_ab_load__()
812- return __pw_ab_exec__(_doc['f'][_fid], _env, globals())
923+ return __pw_ab_exec__(_doc, _doc ['f'][_fid], _env, globals())
813924'''
814925 return ast .parse (source ).body
815926
0 commit comments