Fix: detect and recover from false positive finder patterns#85
Fix: detect and recover from false positive finder patterns#85bashhack wants to merge 1 commit intomakiuchi-d:masterfrom
Conversation
|
@makiuchi-d wanted to give some context on the cumulative changes in this PR and #84 so you can ground the impact, context and opportunity. I ran into the suite of changes (hopefully - improvements!) in the midst of working with TOTP codes, specifically some stress testing around TOTP/QR decoding. At any rate - to measure the impact of the changes, I tested with randomly generated TOTP secrets encoded as QR codes using Before any fixes from the PRs I've posted:
After the PRs really #84 and #85, with #84 being the biggest single factor:
The remaining ~0.1% failures break down for me as:
Root causeFor ~5% of randomly generated TOTP secrets, QR data accidentally contains patterns matching the 1:1:3:1:1 finder pattern ratio. The scanner accepts a false positive as the third finder pattern and Example from a version 8 QR in a 400x400 image: The test I used for reference is provided here: func TestSingleThreadReliability(t *testing.T) {
sizes := []int{200, 300, 400}
runs := 5000
for _, size := range sizes {
failures := 0
for i := 0; i < runs; i++ {
key, err := totp.Generate(totp.GenerateOpts{
Issuer: "TestApp",
AccountName: "test@example.com",
})
if err != nil {
t.Fatalf("generate: %v", err)
}
rawImg, err := key.Image(size, size)
if err != nil {
t.Fatalf("image: %v", err)
}
var buf bytes.Buffer
if err := png.Encode(&buf, rawImg); err != nil {
t.Fatalf("encode: %v", err)
}
img, err := png.Decode(&buf)
if err != nil {
t.Fatalf("decode png: %v", err)
}
if _, err := qrcode.DecodeQRCodeFromImage(img); err != nil {
failures++
}
}
fmt.Printf(" %dx%d: %d/%d failed (%.2f%%)\n", size, size, failures, runs,
float64(failures)*100/float64(runs))
}
} |
Summary
ProcessFinderPatternInfo, this verifies alternating black and white pattern exists between finder pattern centers, rejecting false positives from QR datacomputeDimensionthat both sides of the finder pattern triple must imply the same QR version +/- 1FindExhaustive()method that scans the full image without early termination, collecting all finder pattern candidatesProcessFinderPatternInfofailure, retry with exhaustive scan soSelectBestPatternshas more candidates to pick the geometrically correct tripleContext
For ~5% of randomly generated TOTP secrets, QR data accidentally contains patterns matching the 1:1:3:1:1 finder pattern ratio. The scanner accepts these as a third finder pattern and stops early, never reaching the real one. The resulting wrong triple produces an incorrect dimension and a failed decode.
The timing and dimension checks now detect these false positives. The exhaustive retry recovers by rescanning the full image to find the real patterns. Together they reduce the observed failure rate from ~5% to ~0.1% on randomly generated TOTP QR codes at 200-400px sizes.f
Testing
[x] All
qrcode/detectorandqrcodetests pass[x] Synthetic version 7 test updated with timing patterns
[x]
makeTimingPatterntest helper added for future test construction