Repository navigation
feat(weave): construct eight-to-two positional degeneration candidate #73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ce59b97
0b270ce
e819dd8
c446064
aa48511
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| # Weave eight-circle / two-circle candidate | ||
|
|
||
| Status: **implemented candidate; asymmetric security not established.** | ||
|
|
||
| This construction records the current user-specified boundary without adding a | ||
| selection law that has not been supplied. | ||
|
|
||
| ## Preserved construction | ||
|
|
||
| For each raw byte: | ||
|
|
||
| - the byte is eight bits; | ||
| - one whole circle plus seven derived circles form the full eight-circle key-set state; | ||
| - every circle has an exact 720-degree return; | ||
| - every circle has its own relationship to space, and that relationship belongs to the | ||
| concrete key set rather than to a universal law; | ||
| - the full/private key set contains all eight circles; | ||
| - a degenerate public view exposes exactly two circles at a time; | ||
| - the two public circles produce two transmitted witness bits for each source bit, | ||
| therefore one eight-bit byte produces sixteen transmitted bits. | ||
|
|
||
| The implementation uses exact rational turns: 0 <= phase < 2, with 1 turn equal to | ||
| 360 degrees and 2 turns equal to the complete 720-degree return. This is an executable | ||
| representation of positions, not a claim that the mathematical circle has finitely | ||
| many possible positions. | ||
|
|
||
| No rule is imposed for how key sets choose their circle positions, their relations to | ||
| space, or which two circles are exposed. Those are construction-instance data. | ||
|
|
||
| ## Executable object | ||
|
|
||
| `stages/eight_circle.py` provides: | ||
|
|
||
| - `Circle`: exact 720-degree position, zero/one positions, and key-set-specific space relation; | ||
| - `FullKeySet`: exactly eight distinct circles; | ||
| - `FullKeySet.degenerate(i, j)`: exactly two-circle public projection; | ||
| - `encode_byte`: eight source bits -> sixteen positional witness bits; | ||
| - `decode_byte`: full-key-set recovery API; | ||
| - raw-byte `encode` / `decode` helpers. | ||
|
|
||
| The candidate is deliberately separate from the older transport profile and is not | ||
| silently installed as the complete Weave `bind` law. | ||
|
|
||
| ## Falsification already obtained | ||
|
|
||
| The first independent-bit implementation fails the intended asymmetric property. | ||
|
|
||
| Because each source bit is independently mapped to one of two public two-bit witnesses, | ||
| a holder of the public pair can evaluate the public forward image of 0 and 1 and recover | ||
| every source bit by comparison. The test | ||
| `test_independent_bit_baseline_is_publicly_bruteforceable` preserves that attack as | ||
| required evidence. | ||
|
|
||
| Therefore: | ||
|
|
||
| [ | ||
| 8 \to 2 | ||
| ] | ||
|
|
||
| is implemented as a geometric degeneration, but **independent per-bit projection is | ||
| falsified as the cryptographic relation**. | ||
|
|
||
| This does not falsify the eight-circle construction. It identifies what the next | ||
| construction must actually use: the message-scoped UCHC origin/axis relational state | ||
| must couple the eight circles so that the six omitted circles change the recoverable | ||
| trajectory rather than merely sitting unused in the private object. | ||
|
|
||
| ## hmmm | ||
|
|
||
| The remaining construction is the coupled UCHC relation itself. No additional policy | ||
| decision is introduced here. The next candidate must make the full eight-circle state | ||
| causally necessary for inversion and then be attacked. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,178 @@ | ||
| """Eight-circle / two-circle positional degeneration candidate. | ||
|
|
||
| This module implements only the geometry and byte-expansion candidate now specified | ||
| for Weave: | ||
|
|
||
| * one byte is eight bits; | ||
| * one whole + seven derived circles form the full eight-circle key-set state; | ||
| * every circle uses an exact 720-degree return represented as rational turns in [0, 2); | ||
| * each circle's relation to space is key-set data, not a universal law; | ||
| * a degenerate public key exposes exactly two circles from the eight; | ||
| * each plaintext bit produces one public sheet bit per exposed circle, so one byte | ||
| produces sixteen transmitted bits. | ||
|
|
||
| The construction is deliberately labelled a candidate. The public two-circle map is | ||
| small enough to brute-force per bit, and the tests preserve that attack witness. A | ||
| message-coupled UCHC origin/axis relation is therefore still required before this can | ||
| support an asymmetric-security claim. | ||
| """ | ||
| from __future__ import annotations | ||
|
|
||
| from dataclasses import dataclass | ||
| from fractions import Fraction | ||
| from typing import Iterable | ||
|
|
||
|
|
||
| TURN_720 = Fraction(2, 1) | ||
|
|
||
|
|
||
| def _phase(value: Fraction) -> Fraction: | ||
| if type(value) is not Fraction: | ||
| raise TypeError("circle positions must be exact fractions") | ||
| return value % TURN_720 | ||
|
|
||
|
|
||
| def _bit(value: int) -> int: | ||
| if type(value) is not int or value not in (0, 1): | ||
| raise TypeError("bit must be integer 0 or 1") | ||
| return value | ||
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class Circle: | ||
| """One exact 720-degree circle instance from a concrete key set.""" | ||
|
|
||
| identity: str | ||
| space: Fraction | ||
| zero: Fraction | ||
| one: Fraction | ||
|
|
||
| def __post_init__(self): | ||
| if type(self.identity) is not str or not self.identity: | ||
| raise ValueError("circle identity required") | ||
| object.__setattr__(self, "space", _phase(self.space)) | ||
| object.__setattr__(self, "zero", _phase(self.zero)) | ||
| object.__setattr__(self, "one", _phase(self.one)) | ||
| if self.zero == self.one: | ||
| raise ValueError("zero and one positions must remain distinct") | ||
|
|
||
| def position(self, bit: int, origin: Fraction) -> Fraction: | ||
| """Exact key-set position relative to this circle's space attachment.""" | ||
| _bit(bit) | ||
| return _phase((self.one if bit else self.zero) - self.space + _phase(origin)) | ||
|
|
||
| def public_sheet(self, bit: int, origin: Fraction) -> int: | ||
| """Degenerate 720->two-sheet witness: first or second visible 360-degree return.""" | ||
| return 0 if self.position(bit, origin) < 1 else 1 | ||
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class FullKeySet: | ||
| """Full/private construction instance: exactly eight circles.""" | ||
|
|
||
| circles: tuple[Circle, ...] | ||
|
|
||
| def __post_init__(self): | ||
| if type(self.circles) is not tuple or len(self.circles) != 8: | ||
| raise ValueError("full key set requires exactly eight circles") | ||
| if any(type(circle) is not Circle for circle in self.circles): | ||
| raise TypeError("full key set members must be Circle instances") | ||
| identities = tuple(circle.identity for circle in self.circles) | ||
| if len(set(identities)) != 8: | ||
| raise ValueError("circle identities must be unique") | ||
|
Comment on lines
+80
to
+82
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When callers provide eight arbitrary AGENTS.md reference: AGENTS.md:L22-L26 Useful? React with 👍 / 👎. |
||
|
|
||
| def degenerate(self, left: int, right: int) -> "PublicPair": | ||
| if any(type(i) is not int or not 0 <= i < 8 for i in (left, right)): | ||
| raise ValueError("public circle indices must be integers in [0, 7]") | ||
| if left == right: | ||
| raise ValueError("degeneration requires two distinct circles") | ||
| return PublicPair((left, right), (self.circles[left], self.circles[right])) | ||
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class PublicPair: | ||
| """Exactly two circles exposed by one public degeneration.""" | ||
|
|
||
| indices: tuple[int, int] | ||
| circles: tuple[Circle, Circle] | ||
|
|
||
| def __post_init__(self): | ||
| if (type(self.indices) is not tuple or len(self.indices) != 2 | ||
| or len(set(self.indices)) != 2): | ||
| raise ValueError("public degeneration requires exactly two distinct indices") | ||
| if type(self.circles) is not tuple or len(self.circles) != 2: | ||
| raise ValueError("public degeneration exposes exactly two circles") | ||
| if any(type(circle) is not Circle for circle in self.circles): | ||
| raise TypeError("public circles must be Circle instances") | ||
|
Comment on lines
+103
to
+106
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Useful? React with 👍 / 👎. |
||
|
|
||
| def witness(self, bit: int, origin: Fraction) -> tuple[int, int]: | ||
| _bit(bit) | ||
| return tuple(circle.public_sheet(bit, origin) for circle in self.circles) | ||
|
|
||
|
|
||
| def encode_byte(public: PublicPair, value: int, origin: Fraction) -> tuple[int, ...]: | ||
| """Encode one byte as sixteen public positional witness bits.""" | ||
| if type(public) is not PublicPair: | ||
| raise TypeError("two-circle public key required") | ||
| if type(value) is not int or not 0 <= value <= 255: | ||
| raise ValueError("value must be one byte") | ||
| out: list[int] = [] | ||
| for shift in range(7, -1, -1): | ||
| bit = (value >> shift) & 1 | ||
| zero = public.witness(0, origin) | ||
| one = public.witness(1, origin) | ||
| if zero == one: | ||
| raise ValueError("two-circle degeneration is ambiguous at this origin") | ||
| out.extend(public.witness(bit, origin)) | ||
| return tuple(out) | ||
|
|
||
|
|
||
| def decode_byte(full: FullKeySet, public_indices: tuple[int, int], | ||
| witness: Iterable[int], origin: Fraction) -> int: | ||
| """Recover one byte with the full key-set object. | ||
|
|
||
| Current candidate recovery consults the same selected circles. This is intentional: | ||
| the accompanying attack test demonstrates that this baseline does not yet require | ||
| the six hidden circles and therefore cannot establish the intended asymmetric | ||
| property. | ||
| """ | ||
| if type(full) is not FullKeySet: | ||
| raise TypeError("full eight-circle key set required") | ||
| if type(public_indices) is not tuple or len(public_indices) != 2: | ||
| raise ValueError("exactly two public indices required") | ||
| public = full.degenerate(*public_indices) | ||
| bits = tuple(witness) | ||
| if len(bits) != 16 or any(type(bit) is not int or bit not in (0, 1) for bit in bits): | ||
| raise ValueError("byte witness must contain exactly sixteen integer bits") | ||
|
|
||
| value = 0 | ||
| zero = public.witness(0, origin) | ||
| one = public.witness(1, origin) | ||
| if zero == one: | ||
| raise ValueError("two-circle degeneration is ambiguous at this origin") | ||
| for offset in range(0, 16, 2): | ||
| pair = bits[offset:offset + 2] | ||
| if pair == zero: | ||
| bit = 0 | ||
| elif pair == one: | ||
| bit = 1 | ||
| else: | ||
| raise ValueError("witness is not admitted by this key-set projection") | ||
| value = (value << 1) | bit | ||
| return value | ||
|
|
||
|
|
||
| def encode(data: bytes, public: PublicPair, origin: Fraction) -> tuple[int, ...]: | ||
| """Raw bytes -> exactly twice as many transmitted bits as source bits.""" | ||
| if type(data) is not bytes: | ||
| raise TypeError("raw bytes required") | ||
| return tuple(bit for byte in data for bit in encode_byte(public, byte, origin)) | ||
|
|
||
|
|
||
| def decode(full: FullKeySet, public_indices: tuple[int, int], | ||
| witness: Iterable[int], origin: Fraction) -> bytes: | ||
| bits = tuple(witness) | ||
| if len(bits) % 16: | ||
| raise ValueError("ciphertext witness length must be a multiple of sixteen bits") | ||
| return bytes(decode_byte(full, public_indices, bits[i:i + 16], origin) | ||
| for i in range(0, len(bits), 16)) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| """Tests for the explicit eight-circle -> two-circle Weave candidate. | ||
|
|
||
| These prove construction properties and preserve the current attack witness. They do | ||
| not claim asymmetric security. | ||
| """ | ||
| from fractions import Fraction | ||
| import unittest | ||
|
|
||
| from stages.eight_circle import Circle, FullKeySet, encode, decode, encode_byte | ||
|
|
||
|
|
||
| def circle(i: int, *, shift: int = 0) -> Circle: | ||
| # Exact rational positions over a 720-degree return. 1 turn = 360 degrees. | ||
| return Circle( | ||
| identity=f"g{i}", | ||
| space=Fraction((i * 3 + shift) % 16, 8), | ||
| zero=Fraction((i * 3 + shift + 1) % 16, 8), | ||
| one=Fraction((i * 3 + shift + 9) % 16, 8), | ||
| ) | ||
|
|
||
|
|
||
| def keyset(*, shift: int = 0) -> FullKeySet: | ||
| return FullKeySet(tuple(circle(i, shift=shift) for i in range(8))) | ||
|
|
||
|
|
||
| class EightCircleTests(unittest.TestCase): | ||
| def test_full_key_is_exactly_eight_and_public_is_exactly_two(self): | ||
| full = keyset() | ||
| public = full.degenerate(1, 6) | ||
| self.assertEqual(len(full.circles), 8) | ||
| self.assertEqual(public.indices, (1, 6)) | ||
| self.assertEqual(len(public.circles), 2) | ||
| self.assertEqual(tuple(c.identity for c in public.circles), ("g1", "g6")) | ||
| with self.assertRaises(ValueError): | ||
| full.degenerate(2, 2) | ||
|
|
||
| def test_positions_use_exact_720_degree_return(self): | ||
| c = Circle("x", Fraction(15, 8), Fraction(1, 8), Fraction(9, 8)) | ||
| self.assertEqual(c.position(0, Fraction(1, 4)), Fraction(1, 2)) | ||
| self.assertEqual(c.position(1, Fraction(1, 4)), Fraction(3, 2)) | ||
| self.assertEqual(c.position(0, Fraction(9, 4)), Fraction(1, 2)) | ||
| self.assertEqual(c.public_sheet(0, Fraction(1, 4)), 0) | ||
| self.assertEqual(c.public_sheet(1, Fraction(1, 4)), 1) | ||
|
|
||
| def test_key_set_space_relations_are_data_not_a_global_rule(self): | ||
| origin = Fraction(1, 8) | ||
| a = keyset(shift=0).degenerate(0, 1) | ||
| b = keyset(shift=5).degenerate(0, 1) | ||
| # The API accepts both key sets without privileging either space relation. | ||
| self.assertNotEqual( | ||
| tuple(c.space for c in a.circles), | ||
| tuple(c.space for c in b.circles), | ||
| ) | ||
| self.assertEqual(len(encode_byte(a, 0xA5, origin)), 16) | ||
| self.assertEqual(len(encode_byte(b, 0xA5, origin)), 16) | ||
|
|
||
| def test_raw_bytes_double_in_bit_length_and_roundtrip(self): | ||
| full = keyset() | ||
| public = full.degenerate(0, 3) | ||
| origin = Fraction(3, 16) | ||
| data = bytes(range(256)) | ||
| witness = encode(data, public, origin) | ||
| self.assertEqual(len(witness), len(data) * 16) | ||
| self.assertEqual(decode(full, public.indices, witness, origin), data) | ||
|
|
||
| def test_malformed_or_nonadmitted_witness_fails(self): | ||
| full = keyset() | ||
| public = full.degenerate(0, 3) | ||
| origin = Fraction(3, 16) | ||
| witness = list(encode_byte(public, 0x5A, origin)) | ||
| # Fixture admits only (0,0) and (1,1); mixed pair is impossible. | ||
| witness[0:2] = [0, 1] | ||
| with self.assertRaises(ValueError): | ||
| decode(full, public.indices, witness, origin) | ||
| with self.assertRaises(ValueError): | ||
| decode(full, public.indices, (0,) * 15, origin) | ||
|
|
||
| def test_independent_bit_baseline_is_publicly_bruteforceable(self): | ||
| """Falsification boundary: two public circles alone decode this baseline. | ||
|
|
||
| An attacker can compare each observed two-bit witness with the public forward | ||
| images of 0 and 1. Therefore this independent-bit sheet projection is a useful | ||
| executable geometry scaffold but NOT the intended asymmetric construction. | ||
| The missing requirement is the message-coupled UCHC origin/axis relation in | ||
| which the six omitted circles alter the recoverable trajectory. | ||
| """ | ||
| full = keyset() | ||
| public = full.degenerate(2, 7) | ||
| origin = Fraction(5, 16) | ||
| value = 0xD3 | ||
| witness = encode_byte(public, value, origin) | ||
|
|
||
| recovered = 0 | ||
| code = {public.witness(bit, origin): bit for bit in (0, 1)} | ||
| self.assertEqual(len(code), 2) | ||
| for i in range(0, 16, 2): | ||
| recovered = (recovered << 1) | code[witness[i:i + 2]] | ||
| self.assertEqual(recovered, value) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the intended UCHC circle/space relation is anything other than an additive scalar offset, this formula silently replaces it with
bit_position - space + origin (mod 2), even thoughEIGHT_CIRCLE.mdsays no rule for those relations has been supplied and the existing Weave specification leaves native origin/axis binding open. The resulting round-trip and attack evidence therefore measures an invented geometry; this operation should remainhmmmor be supplied explicitly by a source-backed construction instance.AGENTS.md reference: AGENTS.md:L22-L26
Useful? React with 👍 / 👎.