Design
morning-atlas-1
The level-512 challenge weight leaves a forgery space far short of 512 bits
Minor break · Argued · Cause: parameter size
Property: existential unforgeability
Cost against claim: forgery bound 2322.7 against a 2512 claim
At level 512 the challenge weight is fixed at kappa=60 (params.h KAPPA, spec line 1064), giving a challenge set of only 2322.7 classical (2161.3 quantum). The specification's own sizing rule for kappa, that the challenge set must exceed the targeted security level, holds at level 256 but is not re-applied at 512, and the forgery bound tied to the challenge set falls well short of the claim there.
| Parameter set | Claim | Attack cost | Verdict |
|---|
| MORNING-ATLAS-512 | 512 classical, 256 quantum | 2322.7 classical; 2161.3 quantum | Minor break |
What causes it
|B_kappa| = 2kappa * C(n,kappa); with n=512 and kappa=60 this is 2322.7 classical, 2161.3 quantum (reaching 2512.2 needs kappa>=118). Spec p13, lines 1005-1008, states kappa is chosen so the challenge set exceeds the targeted security level; spec line 1064 gives kappa=31/69/60/60 for levels 128/192/256/512, satisfying that rule at 256 (2257 > 2256) but not at 512.
Evidence
Recomputed independently with a separate script (chal.py, not the submission's own lengths.py): n=512, kappa=60 gives log2|B_kappa|=322.67 classical, 161.34 quantum, checked against params.h and spec line 1064. The forgery-bound argument follows the specification's own FS/ROM reduction to the challenge set; no forgery was executed.
Cause in the specification, traced to the specification text.
Credit
Found by PQC-X.
morning-atlas-2
A 384-bit message digest caps unforgeability below the two highest claims
Minor break · Argued · Cause: parameter size
Property: existential unforgeability
Cost against claim: message-binding collision in 2192 against 256- and 512-bit claims
Signing and verification bind the message only through mu=CRH(CRH(pk)||M), a 384-bit digest with no per-signature salt (spec line 202, CRHBYTES=48 in every params.h; Algorithms 2/3 line 610). A birthday collision on mu, found after one signing query on M, gives a valid signature on a colliding message M', an EUF-CMA forgery, at a cost of 2192.
| Parameter set | Claim | Attack cost | Verdict |
|---|
| MORNING-ATLAS-256 | 256 classical | 2192 | Minor break |
| MORNING-ATLAS-512 | 512 classical | 2192 | Minor break |
What causes it
spec line 202 fixes CRH's output at 48 bytes; CRHBYTES=48U in all four params.h; Algorithms 2/3 line 610 compute mu=CRH(CRH(pk)||M) with no salt, so mu is the only message-dependent input to signing and verification.
Evidence
CRHBYTES and the mu computation were read directly from the specification and confirmed identical in all four params.h. The 2192 birthday bound is a direct combinatorial argument in the ideal model; no collision search was run.
Cause in the specification, traced to the specification text.
Limits
Exactly meets the target at level 192 (2192=192); comfortably exceeds it at level 128.
Credit
Found by PQC-X.
morning-atlas-3
The level-512 master seed is only 256 bits
Minor break · Argued · Cause: parameter size
Property: key recovery
Cost against claim: key recovery in 2256 against a 2512 claim
Key generation draws rho and K, each 256 bits, from a single master seed at every level (Algorithm 1 line 1; SEEDBYTES=32 in lwrdsa512/params.h), and the mask is derived deterministically from K (Algorithm 2 line 8: y=Sam(K||mu||count)). Guessing the 256-bit K predicts the mask for a captured signature and yields the secret s1 from a single signature by the same linear relation already confirmed for the code-level mask bug; equivalently, exhausting the 256-bit master seed regenerates the public key. 256 bits is far short of the 512-bit (256-bit quantum) target the level claims.
| Parameter set | Claim | Attack cost | Verdict |
|---|
| MORNING-ATLAS-512 | 512 classical, 256 quantum | 2256 classical; 2128 quantum | Minor break |
What causes it
Algorithm 1 line 1 draws rho, K <- {0,1}^256 at every level regardless of the target; the secret-key size formula (spec line 1084, "112"=32+32+48 bytes) does not scale with the level; SEEDBYTES=32 in lwrdsa512/params.h. Algorithm 2 line 8 derives the mask deterministically from K.
Evidence
Traced directly in the specification (Algorithm 1 line 1, Algorithm 2 line 8) and in the level-512 params.h and reference sig_keygen/pseudoXOF code. Checked independently by a second member of PQC-X reading the same specification passages and source file. No attack was executed against level 512; levels 128, 192 and 256 already use a 256-bit seed that meets or exceeds their own target and are not affected.
Cause in the specification, traced to the specification text.
Credit
Found by PQC-X.
Implementation
Bugs in the submitted code; the specification is sound on these points.
morning-atlas-i1
rej_gamma1m1 writes the same 20-bit sample to two mask coefficients
Minor break · Demonstrated · Cause: code
Property: key recovery
Cost against claim: secret key recovered from 2 signatures at levels 128, 192 and 256; 512 unaffected
The reference and optimized sampler for the signing mask y (rej_gamma1m1 in poly.c, the GAMMA1<=(1<<19) branch used at levels 128, 192 and 256) draws a fresh 20-bit sample for the second of every pair of coefficients but writes that same sample to both, so accepted masks always satisfy y[2i]=y[2i+1]. That equality turns two ordinary signatures into a full-rank linear system in the secret vector s1, which solves exactly.
| Parameter set | Claim | Attack cost | Verdict |
|---|
| MORNING-ATLAS-128 | 128 classical | 2 signatures, then linear algebra | Minor break |
| MORNING-ATLAS-192 | 192 classical | 2 signatures, then linear algebra | Minor break |
| MORNING-ATLAS-256 | 256 classical | 2 signatures, then linear algebra | Minor break |
| MORNING-ATLAS-512 | 512 classical | not affected | Holds |
What causes it
poly.c, rej_gamma1m1, #if GAMMA1<=(1<<19) branch (taken at 128/192/256, since GAMMA1_BITS=QBITS-4 and QBITS=23 gives GAMMA1=219): the loop reads a 20-bit t twice per pair but keeps only the second value, writing a[ctr++]=Q+GAMMA1-1-t twice in a row. Level 512 redefines QBITS=25 (GAMMA1=221), taking the #elif GAMMA1<=(1<<23) branch, which writes one sample per coefficient. Identical in the reference and optimized code at every affected level.
Evidence
A fresh unmodified build, instrumented only to count adjacent equal pairs in y, gave 384/384 accepted pairs at level 128, 640/640 at 192, 896/896 at 256, and 0/1792 at 512. scripts/recover.py, run on 6 real signatures (logs/dump128.txt), used the pair identity to set up a full-rank linear system and recovered s1 exactly equal to the true secret key; the retraced algebra needs about 2 signatures in the generic case. A fresh, independently built genKAT reproduces the shipped Test_Vectors/KAT_SIG_lwrdsa128.txt byte for byte (sha256 42fc86b6...181d), so the official test vectors already carry the bug. Independently re-derived and re-run by a second PQC-X member on a separate copy of the unmodified reference code.
Cause in the submitted code; the specification is not affected.
Limits
MORNING-ATLAS-512 takes the correct sampler branch and is not affected.
Credit
First public report: Xianhui Lu and Yijian Liu (ngcc.dev sign-15-4, 2026-09-23). Found independently by PQC-X.