The Laws of Cryptography:
The Mod 97 Scheme

by Neal R. Wagner

Copyright © 2001 by Neal R. Wagner. All rights reserved.

NOTE: This site is obsolete. See book draft (in PDF):

Here is the mod 97 scheme used for extra error detection. The check below tests its performance for adjacent double error detection. One expects this check to catch apprximately 99% of all random errors. However, it catches 99.94% of all adjacent double errors (except for possible adjacent double errors involving one of the two decimals representing the check "digit" and the first actual data digit).


// ErrorDetection.java: base class for single-digit error detection
public class ErrorDetection {

   public static void printArray(int[] a) {
      for (int i = 0; i < a.length; i++) {
      	 if (a[i] == 10)
      	    System.out.print("X ");
      	 else
      	    System.out.print(a[i]);
      	 if (i%5 == 0) System.out.print(" ");
      }
      System.out.println();
   }
   
   public static void printUnchecked(int[] a) {
   	  System.out.print("? ");
      for (int i = 1; i < a.length; i++) {
      	 System.out.print(a[i]);
      	 if (i%5 == 0) System.out.print(" ");
      }
      System.out.println();
   }
}

// Mod97ErrorDetection.java: Implement the mod 97 check for error detection // uses successive powers of 10 (modulo 97) for the weights public class Mod97ErrorDetection extends ErrorDetection { public static int insertCheck(int[] a) { int check = 0; int weight = 10; for (int i = 1; i < a.length; i++) { check = (check + weight*a[i])%97; weight = (weight*10)%97; } if (check == 0) a[0] = 0; else a[0] = -check + 97; return a[0]; } public static boolean doCheck(int[] a) { int check = 0; int weight = 1; for (int i = 0; i < a.length; i++) { check = (check + weight*a[i])%97; weight = (weight*10)%97; } if (check != 0) return false; else return true; } // main function public static void main (String[] args) { int[] a = new int[100]; boolean checkFlag = false; // no need for a random start for (int i = 1; i < a.length; i++) a[i] = (int)(Math.random() * 10.0); // try all adjacent double errors int errorCount = 0; int totalCount = 0; // try each successive position (all the same) for (int pos = 1; pos < 99; pos++) // try every pair of digits for the initial pair for (int p1 = 0; p1 < 10; p1++) for (int p2 = 0; p2 < 10; p2++) { // insert the initial pair a[pos] = p1; a[pos+1] = p2; // do the check and insert mod 97 check "digit" Mod97ErrorDetection.insertCheck(a); // try every pair of digits for the double error for (int n1 = 0; n1 < 10; n1++) for (int n2 = 0; n2 < 10; n2++) // only try if an actual change if (n1 != p1 || n2 != p2) { totalCount++; // insert new air as an error a[pos] = n1; a[pos+1] = n2; // check if the change is not detected if (Mod97ErrorDetection.doCheck(a)) { System.out.println("Error, old digits: " + p1 + p2 + ", new digits: " + n1 + n2 + ". Position: " + pos); errorCount++; } } } System.out.println("Adjacent double errors undetected: " + errorCount + ", out of " + totalCount + ", or " + ((double)errorCount/totalCount*100) + "%"); } // end of main }

Here is the output, showing that there are only 6 kinds of adjacent double errors that remain undetected. For example, "10" changed to "89". Here in the weight equation, "10" is an additional 1 + 0*10 = 1 (along with extra power of 10 weight), while "89" is an additional 8 + 9*10 = 98 (along with the same extra power of 10 weight). When the 98 is reduced modulo 97, it also becomes 1, so that the new equation has the same value as the old.

The error rate is 0.060606...%, or the equation catches 99.94 % of all adjacent double errors.


Error, old digits: 00, new digits: 79. Position: 1 Error, old digits: 10, new digits: 89. Position: 1 Error, old digits: 20, new digits: 99. Position: 1 Error, old digits: 79, new digits: 00. Position: 1 Error, old digits: 89, new digits: 10. Position: 1 Error, old digits: 99, new digits: 20. Position: 1 (similar entries for Position = 2, 3, ..., 98) Adjacent double errors undetected: 588, out of 970200, or 0.06060606060606061%