فهرست مقاله
یک بسته میتواند برچسبِ معتبر داشته باشد و همچنان نسخهٔ دوبارهٔ همان بستهٔ قبلی باشد. برای دیدنِ این تفاوت، یک پنجرهٔ پذیرشِ شاخص را با پایتون میسازیم: نامزد را بدونِ تغییرِ حافظه بررسی میکنیم و ثبت را به مرحلهٔ پس از تأییدِ بیرونی میسپاریم. همهٔ شاخصها ساختگیاند؛ برنامه نه بستهٔ شبکه میگیرد و نه رمزنگاری یا وارسیِ اصالت را اجرا میکند.
۱. تأییدِ پیام با تازهبودنِ شاخص یکی نیست

در بخش ۳٫۳ از RFC 3711، بررسیِ فهرستِ بازپخش، تأییدِ برچسب و بهروزرسانیِ وضعیت، مرحلههای جدا هستند. پنجره نباید صرفاً بهخاطرِ رسیدنِ یک نامزدِ بزرگ جلو برود. این درس فقط منطقِ حافظهٔ پذیرش را جدا میکند؛ نامِ تابعِ ثبت، جایِ نتیجهٔ واقعیِ تأیید را نمیگیرد.
در درسِ HMAC برای پیامِ ابزار صوتی، همان پیام و برچسب دو بار درست تشخیص داده میشوند. آن تابع حافظهٔ درخواستها را ندارد. اینجا مسئلهٔ تازه، نگهداشتنِ وضعیتِ شاخصهای پذیرفتهشده است؛ کدِ HMAC یا کلیدِ نمونهٔ آن درس را واردِ این برنامه نمیکنیم و از پنجره هم محرمانگی نتیجه نمیگیریم.
ورودیِ هر مرحله، شاخصِ صحیحِ امتدادیِ ازپیشمعلوم در بازهٔ صفر تا 2**48-1 است. انتخابِ زمینهٔ درست، برآوردِ شاخص از شمارهٔ شانزدهبیتی و تأییدِ واقعی، مسئولیتِ بیروناند. در ردگیریِ شمارهٔ RTP و شکافها، شمارهٔ روی سیم امتداد مییافت و تکرارها شمرده میشدند؛ اینجا شمارهٔ خام را امتداد نمیدهیم و دریافتِ تکراری را ثبت نمیکنیم.
۲. پنجرهٔ شصتوچهاربیتی و دو سوی مرز

عرضِ پنجره را W و بزرگترین شاخصِ ثبتشده را T مینامیم. عرض، تعدادِ جایگاه است، نه ثانیه یا مدتِ صوت. حداقلِ عرضِ SRTP در منبع ۶۴ است؛ سقفِ ۲۵۶، انتخابِ محدودِ آموزشیِ ماست. عرض در عمرِ هر نمونه ثابت میماند؛ تغییرِ آن، بازسازیِ ضمنیِ حافظه یا حفظِ وضعیتِ قبلی تعریف نمیکند.
مقدمهٔ RFC 6479 بازهٔ بستهٔ پنجره را با ابتدا و انتهای شامل توضیح میدهد. قراردادِ ما جایگاههای max(0,T-W+1) تا T را نگه میدارد. این منبع دربارهٔ IPsec است؛ فقط تعریفِ عمومیِ بازه را میگیریم و الگوریتمِ حلقهایِ بدونِ شیفت یا مثالِ زبان C آن را اجرا نمیکنیم.
اگر W برابرِ ۶۴ و T برابرِ ۱۲۷ باشد، ابتدای بازه ۶۴ است. شاخصِ ۶۴، شصتوسه جایگاه عقب است و اگر دیده نشده باشد پذیرفتنی است. شاخصِ ۶۳، شصتوچهار جایگاه عقب است و بهدلیلِ کهنگی رد میشود. ردشدنِ این دومی ثابت نمیکند قبلاً رسیده یا مهاجم آن را فرستاده است؛ بستهٔ مشروعِ بسیار خارج از ترتیب نیز میتواند بیرونِ قرارداد بماند.
بیتِ صفر، جایگاهِ T را نشان میدهد و بیتِ d، جایگاهِ T منهای d را. با جلو رفتن، بیتها شیفت میخورند و با ماسکِ Wبیتی محدود میشوند. پرشِ حداقلِ W، تمامِ جایگاههای قبلی را کنار میگذارد؛ در این شاخه مستقیم عددِ یک ساخته میشود. لازم نیست عددی با اندازهٔ خودِ فاصلهٔ عظیم تولید و سپس حذف کنیم.
۳. بررسیِ نامزد را از ثبتِ تأییدشده جدا کنیم

متدِ check فقط نظرِ فعلی را برمیگرداند: نخستین شاخص، جلوتر، جایگاهِ دیدهنشده در پنجره، تکراری یا خیلی کهنه. حتی بررسیِ شاخصِ بسیار دور، T و بیتها را تغییر نمیدهد. در شاهد، فراخواننده نامزد را کنار میگذارد و هیچ ثبت انجام نمیدهد؛ ما برای این کار، یک تابعِ ساختگیِ «تأییدِ رمزنگاری» نساختهایم.
متدِ commit_authenticated پیششرطِ بیرونیِ روشنی دارد: فراخواننده باید تأییدِ واقعی و ارتباطِ شاخص با همان زمینه را قبلاً برقرار کرده باشد. برنامه توانِ اثباتِ این پیششرط را ندارد. فقط نامزدِ پذیرفتنی را ثبت میکند و برای تکرار یا کهنگی، حالت را دستنخورده نگه میدارد. فراخوانیِ نادرستِ این متد میتواند همان نامزدِ نامعتبر را واردِ حافظه کند.
نتیجهٔ بررسی، مجوزِ دائمی نیست. اگر پس از بررسیِ ۱۰۱، شاخصِ ۲۰۰ ثبت شود، پنجرهٔ ۶۴جایگاهی دیگر ۱۰۱ را در بر ندارد. متدِ ثبت دوباره بر اساسِ وضعیتِ فعلی بررسی میکند و آن را رد میکند. این شاهد کاملاً ترتیبی است؛ قفل، اجرای همزمان یا تضمینِ اتمیبودنِ یک گیرندهٔ واقعی را پیاده نکردهایم.
۴. کدِ کامل و مرجعِ مستقلِ تاریخچه

دریافتهای تأییدشدهٔ ساختگی بهترتیب ۱۰۰، ۱۰۲، ۱۰۱، ۱۰۱، ۱۶۴، ۱۰۰، ۱۰۳ و ۱۰۳ هستند. پس از سه موردِ نخست، بیتها برابرِ هفتاند و تکرارِ ۱۰۱ تغییری نمیدهد. با ثبتِ ۱۶۴، بازه از ۱۰۱ آغاز میشود؛ ۱۰۰ بیرون میافتد، ولی ۱۰۱ و ۱۰۲ هنوز در حافظهاند.
شاخصِ ۱۰۳، جایگاهِ خالیِ درونِ بازه را پر میکند و T عقب نمیرود. نتیجهٔ نهایی، شاخصهای ۱۰۱، ۱۰۲، ۱۰۳ و ۱۶۴ و مقدارِ بیتیِ 7*2**61+1 است. تکرارِ بعدیِ ۱۰۳ نیز رد میشود. فهرستِ شاخصها برای خواناییِ خروجی از بیتها ساخته میشود؛ این فهرست، تاریخچهٔ کامل یا شمارندهٔ اتلافِ شبکه نیست.
"""Finite replay-window teaching model, NOT an SRTP receiver or authenticator.
One externally associated context; caller supplies correct extended indices.
Call commit_authenticated only AFTER actual external authentication succeeded.
check is read-only; its result is never an authorization token. Serial use only.
"""
from itertools import product
import json
import platform
import sys
MAX_INDEX = (1 << 48) - 1
MIN_WIDTH, MAX_WIDTH, MAX_STREAM = 64, 256, 16
def checked_index(index):
if type(index) is not int or not 0 <= index <= MAX_INDEX:
raise ValueError('index must be an exact uint48 integer')
return index
class ReplayWindow:
def __init__(self, width=64):
if type(width) is not int or not MIN_WIDTH <= width <= MAX_WIDTH:
raise ValueError('own width contract is exact int64..256')
self._width, self._highest, self._bits = width, None, 0
def snapshot(self):
high = self._highest
retained = [] if high is None else [high - age for age in range(self._width - 1, -1, -1)
if self._bits & (1 << age)]
return {'width': self._width, 'highest': high,
'lowest': None if high is None else max(0, high - self._width + 1),
'bitmap_decimal': self._bits, 'retained_indices': retained}
def check(self, index):
index = checked_index(index)
if self._highest is None:
reason = 'first'
elif index > self._highest:
reason = 'ahead'
else:
age = self._highest - index
reason = ('too_old' if age >= self._width else
'duplicate' if self._bits & (1 << age) else 'unseen_in_window')
return {'index': index, 'admissible': reason not in ('too_old', 'duplicate'), 'reason': reason}
def commit_authenticated(self, index):
"""Caller attests external authentication; this method does NOT verify it."""
verdict = self.check(index) # Always recheck current state, never an old probe.
if verdict['admissible']:
if self._highest is None:
self._highest, self._bits = index, 1
elif index > self._highest:
jump = index - self._highest
self._bits = (1 if jump >= self._width else
((self._bits << jump) | 1) & ((1 << self._width) - 1))
self._highest = index
else:
self._bits |= 1 << (self._highest - index)
return {**verdict, 'committed': verdict['admissible'], 'state': self.snapshot()}
def run_authenticated(indices, width=64):
"""A bounded supplied fixture stream; no actual authentication performed."""
if type(indices) is not tuple or len(indices) > MAX_STREAM:
raise ValueError('own stream is an exact tuple of at most16 indices')
for index in indices:
checked_index(index)
window = ReplayWindow(width)
return [window.commit_authenticated(index) for index in indices]
def accepted_history_reference(indices, width):
"""Independent complete-history set model; no bit operations or main calls."""
history = set()
trace = []
for index in indices:
high = max(history) if history else None
if high is None:
reason = 'first'
elif index > high:
reason = 'ahead'
elif index < high - width + 1:
reason = 'too_old'
elif index in history:
reason = 'duplicate'
else:
reason = 'unseen_in_window'
accepted = reason in ('first', 'ahead', 'unseen_in_window')
if accepted:
history.add(index)
high = max(history)
low = max(0, high - width + 1)
trace.append({'index': index, 'admissible': accepted, 'reason': reason, 'committed': accepted,
'state': {'width': width, 'highest': high, 'lowest': low,
'retained_indices': sorted(value for value in history if value >= low)}})
return trace
def without_bitmap(trace):
return [{**row, 'state': {key: value for key, value in row['state'].items() if key != 'bitmap_decimal'}}
for row in trace]
def rejects(call):
try:
call()
except ValueError:
return True
return False
def run_example():
indices = (100, 102, 101, 101, 164, 100, 103, 103)
trace = run_authenticated(indices)
final = trace[-1]['state']
empty = ReplayWindow()
zero = empty.commit_authenticated(0)
duplicate = ReplayWindow(); duplicate.commit_authenticated(100)
duplicate_before = duplicate.snapshot(); duplicate_result = duplicate.commit_authenticated(100)
hole = ReplayWindow(); hole.commit_authenticated(100); hole.commit_authenticated(102)
hole_result = hole.commit_authenticated(101)
edge = ReplayWindow(); edge.commit_authenticated(127)
edge_inside, edge_outside = edge.check(64), edge.check(63)
edge_result = edge.commit_authenticated(64)
advance = ReplayWindow(); advance.commit_authenticated(100); advance.commit_authenticated(102)
one_step = advance.commit_authenticated(103)
shift63 = ReplayWindow(); shift63.commit_authenticated(10)
shift63_result = shift63.commit_authenticated(73)
shift64 = ReplayWindow(); shift64.commit_authenticated(10)
shift64_result = shift64.commit_authenticated(74)
wide = ReplayWindow(256); wide.commit_authenticated(0)
huge = wide.commit_authenticated(MAX_INDEX)
maximum_before = wide.snapshot()
wrap_not_invented = wide.commit_authenticated(0)
maximum_rejected = rejects(lambda: wide.commit_authenticated(MAX_INDEX + 1))
extended = run_authenticated((65535, 65536))
uncommitted = ReplayWindow(); uncommitted.commit_authenticated(100)
uncommitted_before = uncommitted.snapshot()
candidate_a, candidate_b = uncommitted.check(MAX_INDEX), uncommitted.check(MAX_INDEX)
# The synthetic caller discards this candidate; no MAC/check is simulated.
uncommitted_unchanged = uncommitted.snapshot() == uncommitted_before
after_discard = uncommitted.commit_authenticated(101)
stale = ReplayWindow(); stale.commit_authenticated(100)
old_probe = stale.check(101)
stale.commit_authenticated(200); stale_before = stale.snapshot()
stale_result = stale.commit_authenticated(101)
class IntChild(int):
pass
class TupleChild(tuple):
pass
bad_indices = (-1, MAX_INDEX + 1, True, False, 1.0, '1', None, IntChild(1))
bad_widths = (0, 63, 257, True, 64.0, '64', None, IntChild(64))
strict = ReplayWindow(); strict.commit_authenticated(20); strict_before = strict.snapshot()
invalid_ok = all(rejects(lambda x=x: strict.check(x)) and
rejects(lambda x=x: strict.commit_authenticated(x)) for x in bad_indices)
widths_ok = all(rejects(lambda x=x: ReplayWindow(x)) for x in bad_widths)
pulls = []
def untouched_generator():
pulls.append('consumed')
yield 1
iterator = untouched_generator()
stream_ok = (rejects(lambda: run_authenticated([1])) and
rejects(lambda: run_authenticated(TupleChild((1,)))) and
rejects(lambda: run_authenticated(iterator)) and not pulls and
rejects(lambda: run_authenticated((1,) * 17)) and
len(run_authenticated((1,) * 16)) == 16)
oracle_ok, oracle_cases, compared_prefixes = True, 0, 0
alphabet = (0, 1, 63, 64, 65, 127, 128)
for width in (64, 65, 128):
for length in range(5):
for fixture in product(alphabet, repeat=length):
observed = run_authenticated(fixture, width)
expected = accepted_history_reference(fixture, width)
oracle_ok &= without_bitmap(observed) == expected
oracle_ok &= all(0 <= row['state']['bitmap_decimal'] < 2 ** width for row in observed)
oracle_cases += 1
compared_prefixes += length
independent = ReplayWindow(); independent.commit_authenticated(4)
returned = independent.commit_authenticated(6); independent_before = independent.snapshot()
returned['state']['retained_indices'].append(-99); returned['state']['highest'] = -1
returned['admissible'] = False
saved_snapshot = independent.snapshot(); saved_snapshot['retained_indices'].clear()
probe_copy = independent.check(5); probe_copy['admissible'] = False
independent_after_copies = independent.snapshot()
committed_after_mutated_probe = independent.commit_authenticated(5)
checks = {
'hand_mixed_trace_and_exact_bitmap': (
[row['reason'] for row in trace] == ['first', 'ahead', 'unseen_in_window', 'duplicate',
'ahead', 'too_old', 'unseen_in_window', 'duplicate']
and [row['committed'] for row in trace] == [True, True, True, False, True, False, True, False]
and final == {'width': 64, 'highest': 164, 'lowest': 101,
'bitmap_decimal': 7 * 2 ** 61 + 1, 'retained_indices': [101, 102, 103, 164]}),
'empty_context_and_zero_index_are_explicit': (
ReplayWindow().snapshot() == {'width': 64, 'highest': None, 'lowest': None,
'bitmap_decimal': 0, 'retained_indices': []}
and zero['committed'] and zero['state']['highest'] == 0 and zero['state']['bitmap_decimal'] == 1),
'duplicate_highest_rejected_without_state_change': (
not duplicate_result['committed'] and duplicate_result['reason'] == 'duplicate'
and duplicate.snapshot() == duplicate_before),
'unseen_hole_commits_without_highest_retreat': (
hole_result['committed'] and hole_result['reason'] == 'unseen_in_window'
and hole_result['state']['highest'] == 102 and hole_result['state']['bitmap_decimal'] == 7),
'distance_W_minus_one_inside_W_outside': (
edge_inside['admissible'] and edge_inside['reason'] == 'unseen_in_window'
and not edge_outside['admissible'] and edge_outside['reason'] == 'too_old'
and edge_result['state']['retained_indices'] == [64, 127]
and edge_result['state']['bitmap_decimal'] == 2 ** 63 + 1),
'single_step_advance_preserves_retained_indices': (
one_step['state']['retained_indices'] == [100, 102, 103]
and one_step['state']['bitmap_decimal'] == 11),
'jump_W_minus_one_retains_W_clears_old_state': (
shift63_result['state']['retained_indices'] == [10, 73]
and shift63_result['state']['bitmap_decimal'] == 2 ** 63 + 1
and shift64_result['state']['retained_indices'] == [74]
and shift64_result['state']['bitmap_decimal'] == 1),
'huge_uint48_jump_keeps_only_bounded_bitmap': (
huge['committed'] and huge['state']['highest'] == MAX_INDEX
and huge['state']['bitmap_decimal'] == 1 and huge['state']['retained_indices'] == [MAX_INDEX]),
'uint48_edge_valid_but_modular_reset_not_invented': (
maximum_rejected and not wrap_not_invented['committed']
and wrap_not_invented['reason'] == 'too_old' and wide.snapshot() == maximum_before
and [row['state']['highest'] for row in extended] == [65535, 65536]),
'uncommitted_readonly_candidate_cannot_evict_state': (
candidate_a == candidate_b and candidate_a['admissible'] and uncommitted_unchanged
and after_discard['state']['retained_indices'] == [100, 101]),
'authenticated_commit_rechecks_stale_candidate': (
old_probe['admissible'] and stale_result['reason'] == 'too_old'
and not stale_result['committed'] and stale.snapshot() == stale_before),
'strict_width_index_and_stream_contracts_preserve_state': (
invalid_ok and widths_ok and stream_ok and strict.snapshot() == strict_before
and ReplayWindow(64).snapshot()['width'] == 64 and ReplayWindow(256).snapshot()['width'] == 256),
'independent_complete_history_set_family_ONE_group': (
bool(oracle_ok) and oracle_cases == 3 * sum(7 ** n for n in range(5))),
'fresh_instances_returned_copies_and_input_unchanged': (
independent_after_copies == independent_before and committed_after_mutated_probe['committed']
and committed_after_mutated_probe['state']['retained_indices'] == [4, 5, 6]
and ReplayWindow().snapshot()['highest'] is None
and indices == (100, 102, 101, 101, 164, 100, 103, 103)),
}
assert len(checks) == 14 and all(type(value) is bool and value for value in checks.values()), checks
return {'python': platform.python_version(), 'dependencies': 'Python standard library only',
'scope': 'Supplied correct already-authenticated uint48 indices, one associated context; no crypto, auth, ROC estimation, receiver compatibility, concurrency or security-deployment validation',
'limits': {'minimum_window_from_SRTP': 64, 'own_maximum_width': 256,
'maximum_index': MAX_INDEX, 'own_supplied_stream_limit': MAX_STREAM},
'hand_authenticated_indices': indices, 'hand_trace': trace, 'hand_final_state': final,
'closed_boundary_fixture': {'highest': 127, 'width': 64, 'inside_probe': edge_inside,
'outside_probe': edge_outside, 'after_inside_commit': edge_result['state']},
'uncommitted_candidate_fixture': {'index': MAX_INDEX, 'candidate': candidate_a,
'state_unchanged_without_commit': uncommitted_unchanged,
'next_actual_supplied_commit': after_discard['state'], 'actual_authentication_performed': False},
'independent_oracle_family': {'widths': [64, 65, 128], 'alphabet': alphabet,
'lengths': [0, 1, 2, 3, 4], 'stream_cases_inside_ONE_group': oracle_cases,
'prefix_comparisons_inside_same_group': compared_prefixes,
'reference': 'complete accepted-index set and prefixmaximum; no bit operations or main calls'},
'checks': checks,
'limitations': ['Caller must actually authenticate and associate context/index before commit.',
'Method name is an external precondition, never an authentication implementation.',
'A too-old unseen legitimate packet can be rejected; this is not proof of replay.',
'No raw RTP parsing/index estimation/key management, modular reset or concurrent receiver guarantee.']}
if __name__ == '__main__':
payload = (json.dumps(run_example(), ensure_ascii=True, indent=2) + '\n').encode('utf-8')
sys.stdout.buffer.write(payload)
فایلِ srtp_replay_window.py فقط کتابخانهٔ استاندارد میخواهد. نسخهٔ واقعی در خروجی ثبت میشود. رابطِ دستهای، tuple واقعی با حداکثر شانزده شاخص میگیرد؛ کلاسِ پنجره حافظهٔ منطقیِ محدود دارد و این سقفِ دسته را به سقفِ عمرِ کلاس تبدیل نمیکند. عددِ شناور، bool، رشته و زیرکلاسِ عدد یا tuple بیصدا تبدیل نمیشوند.
مرجعِ آزمون، همهٔ شاخصهای پذیرفتهشدهٔ تاریخچهٔ کوچک را در مجموعه نگه میدارد. برای هر پیشوند، بزرگترین مقدار و اعضای داخلِ بازه را مستقل حساب میکند؛ نه شیفت و بیتمپ دارد و نه متدهای روشِ اصلی را صدا میزند. تفاوتِ نمایشها کمک میکند خطای حذفِ بیت یا مرزِ بازه، از همان مسیر تکرار نشود.
خانوادهٔ محدودِ آزمون، هفت شاخصِ ثابت، طولهای صفر تا چهار و عرضهای ۶۴، ۶۵ و ۱۲۸ دارد. شمارِ جریانها ۸۴۰۳ است و همگی داخلِ یک گروهاند. چهارده گروهِ نامدار، علاوه بر این مرجع، شاهدِ دستی، مرزِ عرض، پرشِ بزرگ، بررسیِ بیاثر و ثبتِ پس از بررسیِ قدیمی را جدا میسنجند؛ شمارِ حالتها تعدادِ گروهها را زیاد نمیکند.
{
"python": "3.14.5",
"dependencies": "Python standard library only",
"scope": "Supplied correct already-authenticated uint48 indices, one associated context; no crypto, auth, ROC estimation, receiver compatibility, concurrency or security-deployment validation",
"limits": {
"minimum_window_from_SRTP": 64,
"own_maximum_width": 256,
"maximum_index": 281474976710655,
"own_supplied_stream_limit": 16
},
"hand_authenticated_indices": [
100,
102,
101,
101,
164,
100,
103,
103
],
"hand_trace": [
{
"index": 100,
"admissible": true,
"reason": "first",
"committed": true,
"state": {
"width": 64,
"highest": 100,
"lowest": 37,
"bitmap_decimal": 1,
"retained_indices": [
100
]
}
},
{
"index": 102,
"admissible": true,
"reason": "ahead",
"committed": true,
"state": {
"width": 64,
"highest": 102,
"lowest": 39,
"bitmap_decimal": 5,
"retained_indices": [
100,
102
]
}
},
{
"index": 101,
"admissible": true,
"reason": "unseen_in_window",
"committed": true,
"state": {
"width": 64,
"highest": 102,
"lowest": 39,
"bitmap_decimal": 7,
"retained_indices": [
100,
101,
102
]
}
},
{
"index": 101,
"admissible": false,
"reason": "duplicate",
"committed": false,
"state": {
"width": 64,
"highest": 102,
"lowest": 39,
"bitmap_decimal": 7,
"retained_indices": [
100,
101,
102
]
}
},
{
"index": 164,
"admissible": true,
"reason": "ahead",
"committed": true,
"state": {
"width": 64,
"highest": 164,
"lowest": 101,
"bitmap_decimal": 13835058055282163713,
"retained_indices": [
101,
102,
164
]
}
},
{
"index": 100,
"admissible": false,
"reason": "too_old",
"committed": false,
"state": {
"width": 64,
"highest": 164,
"lowest": 101,
"bitmap_decimal": 13835058055282163713,
"retained_indices": [
101,
102,
164
]
}
},
{
"index": 103,
"admissible": true,
"reason": "unseen_in_window",
"committed": true,
"state": {
"width": 64,
"highest": 164,
"lowest": 101,
"bitmap_decimal": 16140901064495857665,
"retained_indices": [
101,
102,
103,
164
]
}
},
{
"index": 103,
"admissible": false,
"reason": "duplicate",
"committed": false,
"state": {
"width": 64,
"highest": 164,
"lowest": 101,
"bitmap_decimal": 16140901064495857665,
"retained_indices": [
101,
102,
103,
164
]
}
}
],
"hand_final_state": {
"width": 64,
"highest": 164,
"lowest": 101,
"bitmap_decimal": 16140901064495857665,
"retained_indices": [
101,
102,
103,
164
]
},
"closed_boundary_fixture": {
"highest": 127,
"width": 64,
"inside_probe": {
"index": 64,
"admissible": true,
"reason": "unseen_in_window"
},
"outside_probe": {
"index": 63,
"admissible": false,
"reason": "too_old"
},
"after_inside_commit": {
"width": 64,
"highest": 127,
"lowest": 64,
"bitmap_decimal": 9223372036854775809,
"retained_indices": [
64,
127
]
}
},
"uncommitted_candidate_fixture": {
"index": 281474976710655,
"candidate": {
"index": 281474976710655,
"admissible": true,
"reason": "ahead"
},
"state_unchanged_without_commit": true,
"next_actual_supplied_commit": {
"width": 64,
"highest": 101,
"lowest": 38,
"bitmap_decimal": 3,
"retained_indices": [
100,
101
]
},
"actual_authentication_performed": false
},
"independent_oracle_family": {
"widths": [
64,
65,
128
],
"alphabet": [
0,
1,
63,
64,
65,
127,
128
],
"lengths": [
0,
1,
2,
3,
4
],
"stream_cases_inside_ONE_group": 8403,
"prefix_comparisons_inside_same_group": 32214,
"reference": "complete accepted-index set and prefixmaximum; no bit operations or main calls"
},
"checks": {
"hand_mixed_trace_and_exact_bitmap": true,
"empty_context_and_zero_index_are_explicit": true,
"duplicate_highest_rejected_without_state_change": true,
"unseen_hole_commits_without_highest_retreat": true,
"distance_W_minus_one_inside_W_outside": true,
"single_step_advance_preserves_retained_indices": true,
"jump_W_minus_one_retains_W_clears_old_state": true,
"huge_uint48_jump_keeps_only_bounded_bitmap": true,
"uint48_edge_valid_but_modular_reset_not_invented": true,
"uncommitted_readonly_candidate_cannot_evict_state": true,
"authenticated_commit_rechecks_stale_candidate": true,
"strict_width_index_and_stream_contracts_preserve_state": true,
"independent_complete_history_set_family_ONE_group": true,
"fresh_instances_returned_copies_and_input_unchanged": true
},
"limitations": [
"Caller must actually authenticate and associate context/index before commit.",
"Method name is an external precondition, never an authentication implementation.",
"A too-old unseen legitimate packet can be rejected; this is not proof of replay.",
"No raw RTP parsing/index estimation/key management, modular reset or concurrent receiver guarantee."
]
}
۵. چهار تمرین دربارهٔ تکرار، کهنگی و تغییرِ حالت

منابع در ۲۸ مهر ۱۴۰۵ خوانده شدند؛ سندِ SRTP مربوط به مارسِ ۲۰۰۴ و سندِ دوم مربوط به ژانویهٔ ۲۰۱۲ است. روزِ دقیقِ انتشار از این سربرگها معلوم نیست. این درس خبرِ تازه یا آزمایشِ امنیتِ استقرار نیست. حالتِ پنجره در حافظهٔ همین نمونه است؛ ذخیرهٔ پایدار، بازیابی پس از راهاندازی یا سیاستِ تعویضِ زمینه نداریم.
پرسش اول: آیا برچسبِ معتبر، پذیرشِ دوباره را لازم میکند؟ پاسخ: خیر؛ اگر همان شاخص قبلاً در پنجره ثبت شده باشد، تکراری رد میشود. رابطهٔ برچسب با پیام و رابطهٔ شاخص با تاریخچه، دو پرسشِ جدا هستند. کدِ حاضر فقط دومی را، با فرضِ تأییدِ بیرونیِ درست، مدل میکند.
پرسش دوم: برای T برابرِ ۱۲۷ و W برابرِ ۶۴، با ۶۴ و ۶۳ چه میشود؟ پاسخ: ۶۴ در مرزِ شامل است و اگر دیده نشده باشد ثبت میشود؛ ۶۳ بیرون است. پس از ثبتِ ۶۴، نسخهٔ بعدیِ همان شاخص تکراری است. نامِ «خیلی کهنه» دربارهٔ انگیزهٔ فرستنده یا دریافتِ قبلیِ ۶۳ حکم نمیدهد.
پرسش سوم: اگر نامزدِ بسیار بزرگ تأیید نشد، چرا نباید پنجره را جلو ببریم؟ پاسخ: جلو بردن، شاخصهای درستِ قبلی را حذف میکند و مسیرِ پذیرشِ بعدی را عوض میکند. در شاهدِ بررسیِ تنها، حالتِ پس از ۱۰۰ حفظ میشود و ثبتِ واقعیِ بعدیِ ۱۰۱، هر دو شاخص را نگه میدارد. برنامه خودِ تأییدِ ناموفق را اجرا یا اندازهگیری نکرده است.
پرسش چهارم: آیا صفرِ روی سیم پس از ۶۵۵۳۵ را مستقیماً وارد کنیم؟ پاسخ: خیر؛ رابط شاخصِ امتدادیِ درست میخواهد. در شاهدِ معلومِ روبهجلو، ورودیِ بعدی ۶۵۵۳۶ است. برآوردِ چرخه و ارتباط با زمینه بیروناند؛ برنامه در انتهای دامنهٔ ۴۸بیتی هم صفر را چرخهٔ تازه حدس نمیزند و به نامِ تعویضِ کلید، تاریخچه را بازنشانی نمیکند.
منابع و مطالعهٔ بیشتر
بررسی منابع: ۲۸ مهر ۱۴۰۵. تحلیلها و پیشنهادهای اجرایی، جمعبندی نویسندهاند.

