#!/usr/bin/env python3 """Independent verifier of the faces-from-keypoints corpus (extensions/avatars.md §12). Written from the extension's text. It shares no code with wai-rs, which generates the corpus. It reads the three files of §7.2 to §7.4 with readers of its own; runs the driver of §7.5 and the generator's arithmetic of §7.6 in Python's integers, operator by operator as §7.6 states them (no operator set, no graph); verifies the origin claims, the player's log and the label policy with the synthesis corpus's second implementation (`synthesis_verify.py`); verifies every signature with WAI's strict Ed25519 rule (`claim-conformance/_ed25519.py`) and every link hash with BLAKE3 written from its specification (`claim-conformance/_blake3.py`); reads §10's codes from the extension; and implements the records of §8 and the steps of §9.2. For every vector it requires what the corpus states: each file's reading, each frame's digests (and, where the corpus gives them, its samples), each case's outcome, reason, place and statements. Then it mutates copies of the corpus, each with one defect, and requires every copy to be refused somewhere. python3 -I avatar_face_verify.py Exit status 0 only if every vector verifies and every mutated copy fails. """ import copy import hashlib import json import os import struct import sys HERE = os.path.dirname(os.path.abspath(__file__)) WAI = os.path.normpath(os.path.join(HERE, "..")) sys.path.insert(0, HERE) sys.path.insert(0, os.path.join(WAI, "claim-conformance")) import synthesis_verify as SV # noqa: E402 import _ed25519 as ED # noqa: E402 from _blake3 import blake3 # noqa: E402 EXTENSION = os.path.join(WAI, "extensions", "avatars.md") REGISTRY = os.path.join(WAI, "registry", "capabilities.json") CAPABILITY = "wai.avatar.face" SIGN_DOMAIN = b"wai:avatar\x01" # §8.1 LINK_DOMAIN = b"wai:avatar-link\x01" # §8.1 OBJECT_DOMAIN = b"moq-jwp:object\x01" # jwp-receipts §2.1 HEADER = 84 # §7.4 Refuse, Bad, need, hexb, integer, members = SV.Refuse, SV.Bad, SV.need, SV.hexb, SV.integer, SV.members def sha(b): return hashlib.sha256(b).digest() class Form(Exception): """A file that does not read (§7.2 to §7.4), with its code.""" def __init__(self, code): super().__init__(code) self.code = code # ---------------------------------------------------------------- §7.2 to §7.4 the files def u16(b, at): return b[at] | b[at + 1] << 8 def i16(b, at): v = u16(b, at) return v - 65536 if v >= 32768 else v def side_ok(v): return 4 <= v <= 512 and v % 4 == 0 def have(b, at, n): if len(b) < at + n: raise Form("truncated") def shapes(k, c1, c2, c3): """§7.2: (cin, cout, stride) of the ten convolutions, in the file's order.""" return [(6 + k, c1, 1), (c1, c2, 2), (c2, c3, 2), (c3, c3, 1), (c3, c3, 1), (c3, 4 * c2, 1), (2 * c2, c2, 1), (c2, 4 * c1, 1), (2 * c1, c1, 1), (c1, 4, 1)] def read_generator(b): """§7.2, in its order.""" have(b, 0, 4) if b[:4] != b"WFG1": raise Form("bad_magic") have(b, 4, 1) if b[4] != 1: raise Form("version") have(b, 5, 17) k, width, height, c1, c2, c3, gauss = b[5], u16(b, 6), u16(b, 8), u16(b, 10), u16(b, 12), u16(b, 14), u16(b, 16) bg = struct.unpack_from("= 1 and bg >= 1): raise Form("field_range") at, convs = 22, [] for cin, cout, stride in shapes(k, c1, c2, c3): nw = cout * cin * 9 have(b, at, 2 * nw + 4 * cout) w = list(struct.unpack_from("<%dh" % nw, b, at)) at += 2 * nw bias = list(struct.unpack_from("<%di" % cout, b, at)) at += 4 * cout convs.append({"cin": cin, "cout": cout, "stride": stride, "w": w, "b": bias}) if at != len(b): raise Form("trailing") return {"k": k, "width": width, "height": height, "c1": c1, "c2": c2, "c3": c3, "gauss": gauss, "bg": bg, "convs": convs} def read_identity(b): """§7.3, in §7.2's order.""" have(b, 0, 4) if b[:4] != b"WFI1": raise Form("bad_magic") have(b, 4, 1) if b[4] != 1: raise Form("version") have(b, 5, 5) k, width, height = b[5], u16(b, 6), u16(b, 8) if not (1 <= k <= 32 and side_ok(width) and side_ok(height)): raise Form("field_range") n = 4 * k + width * height * 3 have(b, 10, n) kp = [(i16(b, 10 + 4 * j), i16(b, 12 + 4 * j)) for j in range(k)] if len(b) != 10 + n: raise Form("trailing") return {"k": k, "width": width, "height": height, "keypoints": kp, "rgb": bytes(b[10 + 4 * k:])} def read_segment(b): """§7.4, in its order.""" have(b, 0, 4) if b[:4] != b"WFK1": raise Form("bad_magic") have(b, 4, 1) if b[4] != 1: raise Form("version") have(b, 5, HEADER - 5) k, step, order = b[5], b[6], b[7] first = struct.unpack_from("= 1 and den >= 1 and reserved == 0): raise Form("field_range") have(b, HEADER, 4 * k) cur = [i16(b, HEADER + 2 * j) for j in range(2 * k)] def check(c): if not -32768 <= c * step <= 32767: raise Form("field_range") for c in cur: check(c) frames = [[(cur[2 * j] * step, cur[2 * j + 1] * step) for j in range(k)]] data, pos = b[HEADER + 4 * k:], [0] def bit(): if pos[0] // 8 >= len(data): raise Form("truncated") v = (data[pos[0] // 8] >> (7 - pos[0] % 8)) & 1 pos[0] += 1 return v def bits(m): v = 0 for _ in range(m): v = v << 1 | bit() return v def code(): z = 0 while bit() == 0: z += 1 if z > 31: raise Form("field_range") m = (1 << z) | bits(z) return (m - 1) << order | bits(order) for _ in range(1, n): for i in range(2 * k): u = code() cur[i] += u >> 1 if u % 2 == 0 else -((u + 1) >> 1) check(cur[i]) frames.append([(cur[2 * j] * step, cur[2 * j + 1] * step) for j in range(k)]) if HEADER + 4 * k + (pos[0] + 7) // 8 != len(b): raise Form("trailing") while pos[0] % 8: if bit(): raise Form("noncanonical") return {"k": k, "step": step, "order": order, "generator": bytes(b[8:40]), "identity": bytes(b[40:72]), "first": first, "rate": [num, den], "keypoints": frames} # ---------------------------------------------------------------- §7.5 the driver def exp_neg_q16(x): """integer-operators §2.""" u = -x n = u // 45426 r = x + 45426 * n t = r + 88670 q = (t * t) >> 16 y = ((23495 * q) >> 16) + 22544 return 0 if n >= 63 else y >> n def rdiv(n, d): """sgn(n)·⌊(2|n| + d) / (2d)⌋, d ≥ 1.""" a = (2 * abs(n) + d) // (2 * d) return -a if n < 0 else a def warp(ident, drv, gauss, bg): """§7.5: the warped picture (RGB8 bytes) and the maps ([K][h][w] integers, flat).""" w, h, k, ref, src = ident["width"], ident["height"], ident["k"], ident["rgb"], ident["keypoints"] out = bytearray(w * h * 3) maps = [0] * (k * w * h) for y in range(h): py = 16 * y + 8 for x in range(w): px = 16 * x + 8 den, nx, ny = bg, 0, 0 for j in range(k): dx, dy = px - drv[j][0], py - drv[j][1] wj = exp_neg_q16(-(((dx * dx + dy * dy) * gauss + 128) >> 8)) den += wj nx += wj * (src[j][0] - drv[j][0]) ny += wj * (src[j][1] - drv[j][1]) maps[(j * h + y) * w + x] = wj >> 8 ux, uy = 16 * x + rdiv(nx, den), 16 * y + rdiv(ny, den) ix, iy = ux // 16, uy // 16 fx, fy = ux - 16 * ix, uy - 16 * iy x0, x1 = min(max(ix, 0), w - 1), min(max(ix + 1, 0), w - 1) y0, y1 = min(max(iy, 0), h - 1), min(max(iy + 1, 0), h - 1) for c in range(3): v = ((16 - fx) * (16 - fy) * ref[(y0 * w + x0) * 3 + c] + fx * (16 - fy) * ref[(y0 * w + x1) * 3 + c] + (16 - fx) * fy * ref[(y1 * w + x0) * 3 + c] + fx * fy * ref[(y1 * w + x1) * 3 + c] + 128) >> 8 out[(y * w + x) * 3 + c] = v return bytes(out), maps # ---------------------------------------------------------------- §7.6 the generator's arithmetic def sigmoid_entry(v): """§7.6: T[v + 32768].""" if v == -32768: return 0 if v < 0: return 32768 - sigmoid_entry(-v) d = 65536 + min(exp_neg_q16(-16 * v), 65536) return ((1 << 31) + d // 2) // d TABLE = {} def table(v): if v not in TABLE: TABLE[v] = sigmoid_entry(v) return TABLE[v] def conv(planes, h, w, layer): """A 3 × 3 convolution, zero padding 1, of planes (each a flat list of h·w integers), each output R_half_up((sum + bias), 12).""" cin, cout, stride, wt, bias = layer["cin"], layer["cout"], layer["stride"], layer["w"], layer["b"] wp = w + 2 n = h * wp padded = [] for p in planes: q = [0] * ((h + 2) * wp + 2) for y in range(h): q[(y + 1) * wp + 1:(y + 1) * wp + 1 + w] = p[y * w:(y + 1) * w] padded.append(q) ho, wo = (h - 1) // stride + 1, (w - 1) // stride + 1 out = [] if stride == 1: # Each tap reads the padded plane shifted by its offset; the sums run over the # padded width, whose two extra columns are dropped at the end. for o in range(cout): acc = [bias[o]] * n for ci in range(cin): src = padded[ci] base = (o * cin + ci) * 9 for t in range(9): v = wt[base + t] if v: off = (t // 3) * wp + t % 3 acc = [a + v * s for a, s in zip(acc, src[off:off + n])] out.append([(acc[y * wp + x] + 2048) >> 12 for y in range(ho) for x in range(wo)]) return out, ho, wo # Stride 2: each tap's samples gathered once per input channel. taps = [[[src[(stride * y + t // 3) * wp + stride * x + t % 3] for y in range(ho) for x in range(wo)] for t in range(9)] for src in padded] for o in range(cout): acc = [bias[o]] * (ho * wo) for ci in range(cin): base = (o * cin + ci) * 9 for t in range(9): v = wt[base + t] if v: acc = [a + v * s for a, s in zip(acc, taps[ci][t])] out.append([(a + 2048) >> 12 for a in acc]) return out, ho, wo def relu(planes): return [[min(max(v, 0), 32767) for v in p] for p in planes] def shuffle(planes, h, w): """pixel_shuffle(2): channel 4c + 2i + j to row offset i and column offset j of channel c.""" out = [] for c in range(len(planes) // 4): p = [0] * (4 * h * w) for i in range(2): for j in range(2): src = planes[4 * c + 2 * i + j] for y in range(h): p[(2 * y + i) * 2 * w + j:(2 * y + i) * 2 * w + 2 * w:2] = src[y * w:(y + 1) * w] out.append(p) return out def generate_frame(gen, ident, drv): """§7.7: one frame, RGB8 bytes, and the warped picture and maps it was made from.""" w, h, k = gen["width"], gen["height"], gen["k"] warped, maps = warp(ident, drv, gen["gauss"], gen["bg"]) n = w * h t3 = [[(warped[i * 3 + c] - 128) * 256 for i in range(n)] for c in range(3)] t4 = [[(ident["rgb"][i * 3 + c] - 128) * 256 for i in range(n)] for c in range(3)] t5 = [[v * 128 for v in maps[j * n:(j + 1) * n]] for j in range(k)] cv = gen["convs"] e1, h1, w1 = conv(t3 + t4 + t5, h, w, cv[0]) e1 = relu(e1) e2, h2, w2 = conv(e1, h1, w1, cv[1]) e2 = relu(e2) e3, h3, w3 = conv(e2, h2, w2, cv[2]) e3 = relu(e3) m1, _, _ = conv(e3, h3, w3, cv[3]) m1 = relu(m1) m2, _, _ = conv(m1, h3, w3, cv[4]) r = relu([[a + b for a, b in zip(p, q)] for p, q in zip(m2, e3)]) u2, _, _ = conv(r, h3, w3, cv[5]) u2 = relu(shuffle(u2, h3, w3)) d2, _, _ = conv(u2 + e2, h2, w2, cv[6]) d2 = relu(d2) u1, _, _ = conv(d2, h2, w2, cv[7]) u1 = relu(shuffle(u1, h2, w2)) d1, _, _ = conv(u1 + e1, h1, w1, cv[8]) d1 = relu(d1) o, _, _ = conv(d1, h1, w1, cv[9]) out = bytearray(3 * n) for i in range(n): m = table(min(max(o[3][i], -32767), 32767)) for c in range(3): g = min(max(o[c][i], -32768), 32767) a = (t3[c][i] * m + 16384) >> 15 b = (g * (32768 - m) + 16384) >> 15 out[i * 3 + c] = min(max(((a + b + 128) >> 8) + 128, 0), 255) return bytes(out), warped, maps FRAMES = {} def frame_of(gen_bytes, ident_bytes, drv): """A frame, kept by the generator's and the identity's digests and the keypoints.""" key = (sha(gen_bytes), sha(ident_bytes), tuple(drv)) if key not in FRAMES: FRAMES[key] = generate_frame(read_generator(gen_bytes), read_identity(ident_bytes), drv) return FRAMES[key] def maps_bytes(maps): return b"".join(struct.pack("", file=sys.stderr) sys.exit(2) sys.exit(main(sys.argv[1]))