Skip to the content.

Weigand data similar to H10301 format cards

<Query Kind="Program">
  <Namespace>System</Namespace>
  <Namespace>System.Collections.Generic</Namespace>
  <Namespace>System.IO</Namespace>
  <Namespace>System.Linq</Namespace>
  <Namespace>System.Reflection</Namespace>
</Query>

/*
  Use linq-pad to run the program [https://www.linqpad.net/Download.aspx]
  Script to generate wiegand data from a card number and facility code
  This code generates wiegand data similar to Iris ID card reader.
  
  Use to test weigand generation.
    - This is tested with H10301 format cards
	
  References:
  1. https://stackoverflow.com/a/5612479/18169
  2. https://github.com/jessety/wiegand-encoder/blob/main/src
  3. https://stackoverflow.com/a/2955001/18169
  
  Wiegand protocol is a data-format in which access readers and communicate with each other over a Wiegand interface. 
  There are many different variations of the data format used in the data transmission process.
  The most common is the 26 bit Wiegand format that has 16 bits of ID code, 8 bits of facility code, one bit of leading parity, and a trailing parity bit.
  It is an open format, widely used and accepted by all the access control systems in the world.
  
  PFFFFFFFFNNNNNNNNNNNNNNNNP
  P: Parity bits
  F: Facility code
  N: Card ID
*/
void Main()
{
	var cardNum = 5561;
	var facility = 101;
	
	var binary = Encode(cardNum, facility);
	var hex = BinaryStringToHexString(binary);
	
	cardNum.Dump();
	hex.Dump();
	binary.Dump();
}

//https://stackoverflow.com/a/5612479/18169
static string BinaryStringToHexString(string binary)
{
    if (string.IsNullOrEmpty(binary))
        return binary;

    var result = new StringBuilder(binary.Length / 8 + 1);

    int mod4Len = binary.Length % 8;
    if (mod4Len != 0)
    {
        // pad to length multiple of 8
        binary = binary.PadRight(((binary.Length / 8) + 1) * 8, '0');
    }

    for (int i = 0; i < binary.Length; i += 8)
    {
        string eightBits = binary.Substring(i, 8);
        result.AppendFormat("{0:X2}", Convert.ToByte(eightBits, 2));
    }

    return result.ToString();
}

//https://github.com/jessety/wiegand-encoder/blob/main/src/encode.ts
string Encode(int cardNumber, int facilityCode, int cardNumberLength = 16, int facilityCodeLength = 8) {

  if (cardNumber < 0) {
    throw new Exception($"Card number must be a positive integer. Received {cardNumber}");
  }

  if (facilityCode < 0) {
    throw new Exception($"Facility code must be a positive integer. Received {facilityCode}");
  }
  
  // Create binary representations of the card number and facility code
  // Pad the card number to 16 bits, and the facility code to 8 bits
  //https://stackoverflow.com/a/2955001/18169
  var cardNumberBinary = Convert.ToString(cardNumber, 2).PadLeft(cardNumberLength, '0');
  var facilityCodeBinary = Convert.ToString(facilityCode, 2).PadLeft(facilityCodeLength, '0');

  if (cardNumberBinary.Length > cardNumberLength) {
    throw new Exception($"Card number {cardNumber} is too large to encode in ${cardNumberLength + facilityCodeLength + 2}-bit format.");
  }

  if (facilityCodeBinary.Length > facilityCodeLength) {

    throw new Exception($"Facility code {facilityCode} is too large to encode in ${cardNumberLength + facilityCodeLength + 2}-bit format.");
  }

  // Concatenate the binary representations of the facility code and the card number to create the message
  var credential = facilityCodeBinary + cardNumberBinary;

  // Calculate parity for each half of the message string
  var (left, right) = Calculate(credential);

  // Concatenate the left parity bit, the facility code, the card number, and the right parity bit
  var message = left + credential + right;
  return message;
}

//https://github.com/jessety/wiegand-encoder/blob/main/src/parity.ts
(byte, byte) Calculate(string contents){

  if (contents.Length < 2) {
    throw new Exception("Invalid data");
  }

  if (contents.Length % 2 == 1) {
    throw new Exception("Cannot calculate parity for message with an odd number of bits.");
  }

  // Split the string in half
  var leftHalf = contents.Substring(0, contents.Length / 2);
  var rightHalf = contents.Substring(contents.Length / 2);

  // Calculate how many digits are '1' in each half of the string
  var leftBitCount = leftHalf.Where(bit => bit == '1').Count();
  var rightBitCount = rightHalf.Where(bit => bit == '1').Count();

  // If the left digit count is odd, set the left parity count to '1'. Otherwise, set it to '0'.
  byte left = 0;

  if (leftBitCount % 2 == 1) {
    left = 1;
  }

  // If the right digit count is odd, set the right parity to '0'. Otherwise, set it to '1'.
  byte right = 1;

  if (rightBitCount % 2 == 1) {
    right = 0;
  }

  return (left, right);
}