Appendix D: Solutions for Chapter 23
This page collects solutions and editorial notes for the exercises in Chapter 23: SQIsign in a toy setting. Compute and derivation exercises have worked solutions; open-ended exercises have an editorial note describing what a strong answer addresses.
The fuller versions of these routines are in the sqisign package under solutions/ch23-sqisign. From a clone of the companion repository, pytest tests/ch23 runs its suite. Appendix C has the setup.
Exercise 1
Section titled “Exercise 1”In at the rules are , , and . Anticommuting the units fixes the six cross products, and the two involving both and are the ones that carry the prime:
| Product | Value | Product | Value |
|---|---|---|---|
Expanding against term by term with those substitutions, at :
from fractions import Fraction
p = 431
def quat(a, b, c, d): return (Fraction(a), Fraction(b), Fraction(c), Fraction(d))
def quat_mul(x, y, p): a, b, c, d = x e, f, g, h = y return (a*e - b*f - p*c*g - p*d*h, a*f + b*e + p*c*h - p*d*g, a*g + c*e - b*h + d*f, a*h + d*e + b*g - c*f)
alpha = quat(2, 3, 1, 0) # 2 + 3i + jbeta = quat(1, -1, 0, 1) # 1 - i + kab = quat_mul(alpha, beta, p)ba = quat_mul(beta, alpha, p)print([int(t) for t in ab])# ==> [5, 432, -2, 3]print([int(t) for t in ba])# ==> [5, -430, 4, 1]print(ab[0] == ba[0], ab[1:] == ba[1:])# ==> True FalseThe scalar parts agree at 5 and all three of the , , parts differ, which is what the exercise asks you to confirm. Both scalars come from . The two terms that could have contributed a multiple of are and , and each vanishes here, because carries no component and carries no component. The reduced trace is in both orderings, so equal scalar parts is equal traces, which is the cyclic identity .
The coefficient is where the table earns its keep. The gap between and is , and it is one product: ‘s against ‘s contributes in one ordering and in the other. Get that one sign backwards and both answers move by while every other component stays put, so the trace check still passes and the error survives.
Exercise 2
Section titled “Exercise 2”The standard maximal order at has -basis (Chapter 23). Writing , the chapter’s coordinate test is: iff , , , and are all integers. The -coordinates in are then , , , .
(a) , so and . Not in .
(b) has . Then and , all integers, so it is in . Its coordinates are , since .
(c) has , , , . Then , , , , all integers, so it is in with coordinates : it is exactly .
(d) has , . Then but : the half-integer on must be carried with , as , not on its own. Not in .
Exercise 3
Section titled “Exercise 3”The supersingular -invariant count in characteristic is , with set by . At , , which gives and a count of .
print(83 % 12, 83 // 12 + 2)# ==> 11 8Two things about the run are worth reporting, and both differ from what a reader might guess.
The base curve’s -invariant is not . It is , so BFS starts from , exactly as the chapter’s run starts from .
The search finishes much faster than at . Walking degree-2 and degree-3 edges from over , the frontier sizes are 1, 3, 4: all eight -invariants are reached at depth 2, and the diameter of the combined graph is 2. The graph is small enough that almost everything is a neighbour of a neighbour, so there is no room for the four or five levels the graph needs. A correct implementation prints exactly eight distinct values:
Six of the eight lie in and the last two do not. They are a Frobenius-conjugate pair, with , which is the pair that Deuring’s correspondence sends to a single conjugacy class of maximal orders. The chapter’s refinement of the theorem is visible in an eight-vertex graph you can print.
Exercise 4
Section titled “Exercise 4”Signature lengths do not grow, and the exercise is worth running rather than reasoning about, because the natural guess is wrong. Measured on the shipped package at with the alice key, signing six messages at each setting:
| Challenge walk length | Signature path lengths |
|---|---|
| 3 | 3, 2, 3, 2, 2, 2 |
| 5 | 2, 3, 3, 2, 2, 2 |
| 7 | 2, 3, 2, 2, 2, 2 |
The reason is that the challenge walk and the signature are measuring different things. A longer challenge walk lands on a different , not a farther one: after a few steps the walk is somewhere effectively arbitrary in a 37-vertex graph, and going further does not push it away from . The signature is then a shortest path found by BFS, so its length is bounded by the diameter of the graph, which is 4 at . That bound does not depend on the challenge walk at all.
Verification still succeeds, on one condition: signing and verification must read the same walk length, because the verifier recomputes from scratch. Change CHALLENGE_WALK_LENGTH and rerun tests/ch23/test_sqisign_roundtrip.py and the round trip holds. Sign at length 5 and verify at length 3 and verify returns False, because the verifier walks to a different challenge curve and the transmitted path lands nowhere near . That asymmetry, not the signature size, is what the exercise exposes.
The round-2 SQIsign parameter sets fixed the challenge isogeny degree at with , and the challenge space at with (The SQIsign Team, 2025, sec. 10.2); version 3.0 shortens the challenge walk to length and gives the response odd degree, the one cryptographically relevant change between the rounds (The SQIsign Team, 2026, sec. 1.4). Modifying either in the toy is a structural exploration, not a deployable change.
Exercise 5
Section titled “Exercise 5”ML-DSA’s public key is a 32-byte seed for the matrix plus the vector of ring elements in . ML-DSA-44 has , and keeps 10 bits per coefficient after bits are dropped, so each ring element packs into bytes. That gives bytes, and the explicit data is all but 32 of them (Tables 1 and 2 in National Institute of Standards and Technology, 2024). Note the parameter set: ML-DSA-44 is FIPS 204 security category 2, while SQIsign level 1 targets category 1, so this comparison crosses categories.
SQIsign’s public key is one curve. The specification encodes a curve by its Montgomery coefficient , and appends a one-byte hint used to regenerate a torsion basis faster during verification. At NIST level 1 the round-3 prime is , which is 326 bits, so each element takes bytes and takes 82. With the hint byte that is the 83-byte public key (The SQIsign Team, 2026, sec. 5.2 and Chapter 6). The round-2 prime had 251 bits, 32-byte elements and a 65-byte key by the same arithmetic (The SQIsign Team, 2025).
The asymmetry comes from what each scheme has to commit to. ML-DSA commits to enough lattice structure to make forgery hard, which forces a vector with hundreds of coefficients per security level. SQIsign commits only to the endpoint of an isogeny walk, in a graph with about vertices. The secret is the path, not the endpoint. A single point in that graph is one element however large the graph is, which is why the key does not grow with the security level the way a lattice key does. Level 3 costs 129 bytes and level 5 costs 169.
The round-3 specification, version 3.0 of 1 September 2026, is the current public source for every SQIsign figure on this page; its parameter increase over round 2 is the response to the heuristic endomorphism-ring algorithm of July 2026, which Chapter 23’s cryptanalysis section covers (The SQIsign Team, 2026).