LEGEND MORTAL
LEGEND MORTAL

Reputation: 336

How to Verify Mobile no and email id from Secure QR code of Aadhaar card

Official Document for How to Read QR code data from Secure QR code of UIDAI Aadhaar card: https://uidai.gov.in/images/resource/User_manulal_QR_Code_15032019.pdf

Have used zxing scanner to scan Secure QR code, And was able to get details of aadhar card with the help of follow project in Github: https://github.com/dimagi/AadharUID

Some how I figured out how to extract photo and bit_indicator_value from converted byte array with the help of instructions on document

But I am unable to get hashed exact hashed value of email and mobile from Secure QR code of Aadhar card byte array to verify

I am bit confused from this line of above mentioned document in page no 6:

  1. if its 3 then first read mobile from index (Byte array length – 1-256) and then email from index (Byte array length – 1- 256- 32) in reverse order. Each value will be of fix size of 32 byte.
  2. If Email_mobile_present_bit_indicator_value is 1 then only mobile is present.
  3. If Email_mobile_present_bit_indicator_value is 2 then only email is present.
  4. If Email_mobile_present_bit_indicator_value is 0 then no mobile or email present.

I have also prepared last step in document page no 6 but the users mobile/email hash value and documents mobile/email byte arrays hash value never matches

Code snippet:

    public ScanResult(String input) {
        rawString = input;

        // copied from http://www.java-samples.com/showtutorial.php?tutorialid=152
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        Document dom;
        try {

            //Using factory get an instance of document builder
            DocumentBuilder db = dbf.newDocumentBuilder();
            // Replace </?xml... with <?xml...
            if (input.startsWith("</?")) {
                input = input.replaceFirst("</\\?", "<?");
            }
            // Replace <?xml...?"> with <?xml..."?>
            input = input.replaceFirst("^<\\?xml ([^>]+)\\?\">", "<?xml $1\"?>");
            //parse using builder to get DOM representation of the XML file
            dom = db.parse(new ByteArrayInputStream(input.getBytes("UTF-8")));

        } catch (ParserConfigurationException | SAXException | IOException e) {
            dom = null;
        }

        if (rawString.matches("[0-9]*")) {
            type = QR_CODE_TYPE_SECURE;
            byte[] msgInBytes = null;
            try {
                msgInBytes = decompressByteArray(new BigInteger(rawString).toByteArray());
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (msgInBytes != null) {
                int[] delimiters = locateDelimiters(msgInBytes);
                String referenceId = getValueInRange(msgInBytes, delimiters[0] + 1, delimiters[1]);
                uid = referenceId.substring(0, 4);
                name = getValueInRange(msgInBytes, delimiters[1] + 1, delimiters[2]);
                dob = formatDate(getValueInRange(msgInBytes, delimiters[2] + 1, delimiters[3]),
                        new String[] {"dd-MM-yyyy", "dd/MM/yyyy"});
                yob = dob.substring(0, 4);
                gender = getValueInRange(msgInBytes, delimiters[3] + 1, delimiters[4]);
                co = getValueInRange(msgInBytes, delimiters[4] + 1, delimiters[5]);
                dist = getValueInRange(msgInBytes, delimiters[5] + 1, delimiters[6]);
                lm = getValueInRange(msgInBytes, delimiters[6] + 1, delimiters[7]);
                house = getValueInRange(msgInBytes, delimiters[7] + 1, delimiters[8]);
                loc = getValueInRange(msgInBytes, delimiters[8] + 1, delimiters[9]);
                pc = getValueInRange(msgInBytes, delimiters[9] + 1, delimiters[10]);
                po = getValueInRange(msgInBytes, delimiters[10] + 1, delimiters[11]);
                state = getValueInRange(msgInBytes, delimiters[11] + 1, delimiters[12]);
                street = getValueInRange(msgInBytes, delimiters[12] + 1, delimiters[13]);
                subdist = getValueInRange(msgInBytes, delimiters[13] + 1, delimiters[14]);
                vtc = getValueInRange(msgInBytes, delimiters[14] + 1, delimiters[15]);
                statusCode = STATUS_SUCCESS;
            } else {
                statusCode = STATUS_PARSE_ERROR;
                uid = "";
                name = "";
                gender = "";
                yob = "";
                co = "";
                house = "";
                street = "";
                lm = "";
                loc = "";
                vtc = "";
                po = "";
                dist = "";
                subdist = "";
                state = "";
                pc = "";
                dob = "";
            }
        } else {
            type = QR_CODE_TYPE_UNKNOWN;
            statusCode = STATUS_PARSE_ERROR;
            uid = "";
            name = "";
            gender = "";
            yob = "";
            co = "";
            house = "";
            street = "";
            lm = "";
            loc = "";
            vtc = "";
            po = "";
            dist = "";
            subdist = "";
            state = "";
            pc = "";
            dob = "";
        }

        dobGuess = getDobGuess(dob, yob);
        statusText = getStatusText(statusCode);
    }

private static int[] locateDelimiters(byte[] msgInBytes) {
        int[] delimiters = new int[NUMBER_OF_PARAMS_IN_SECURE_QR_CODE + 1];
        int index = 0;
        int delimiterIndex;
        for (int i = 0; i <= NUMBER_OF_PARAMS_IN_SECURE_QR_CODE; i++) {
            delimiterIndex = getNextDelimiterIndex(msgInBytes, index);
            delimiters[i] = delimiterIndex;
            index = delimiterIndex + 1;
        }
        return delimiters;
    }


    private static String getValueInRange(byte[] msgInBytes, int start, int end) {
        return new String(Arrays.copyOfRange(msgInBytes, start, end), Charset.forName("ISO-8859-1"));
    }

    private static int getNextDelimiterIndex(byte[] msgInBytes, int index) {
        int i = index;
        for (; i < msgInBytes.length; i++) {
            if (msgInBytes[i] == -1) {
                break;
            }
        }
        return i;
    }

    private static byte[] decompressByteArray(byte[] bytes) throws IOException {
        java.io.ByteArrayInputStream bytein = new ByteArrayInputStream(bytes);
        java.util.zip.GZIPInputStream gzin = new GZIPInputStream(bytein);
        java.io.ByteArrayOutputStream byteout = new ByteArrayOutputStream();

        int res = 0;
        byte buf[] = new byte[1024];
        while (res >= 0) {
            res = gzin.read(buf, 0, buf.length);
            if (res > 0) {
                byteout.write(buf, 0, res);
            }
        }
        return byteout.toByteArray();
    }

    private String formatDate(String rawDateString, String[] possibleFormats) {
        if (rawDateString.equals("")) {
            return "";
        }
        SimpleDateFormat toFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date date = null;
        ParseException parseException = null;
        for (String fromFormatPattern : possibleFormats) {
            try {
                SimpleDateFormat fromFormat = new SimpleDateFormat(fromFormatPattern);
                date = fromFormat.parse(rawDateString);
                break;
            } catch (ParseException e) {
                parseException = e;
            }
        }
        if (date != null) {
            return toFormat.format(date);
        } else if (parseException != null) {
            System.err.println("Expected dob to be in dd/mm/yyyy or yyyy-mm-dd format, got " + rawDateString);
            return rawDateString;
        } else {
            throw new AssertionError("This code is unreachable");
        }
    }

    @NonNull
    protected String formatGender(String gender) throws ParseException {
        String lowercaseGender = gender.toLowerCase();
        if (lowercaseGender.equals("male") || lowercaseGender.equals("m")) {
            return "M";
        } else if (lowercaseGender.equals("female") || lowercaseGender.equals("f")) {
            return "F";
        } else if (lowercaseGender.equals("other") || lowercaseGender.equals("o")) {
            return "O";
        } else {
            throw new ParseException("404 gender not found", 0);
        }
    }

    @NonNull
    private String getStatusText(int statusCode) {
        switch (statusCode) {
            case ScanResult.STATUS_SUCCESS:
                return "✓";
            default:
                return "✗";
        }
    }

    @NonNull
    private String getDobGuess(String dob, String yob) {
        if (dob.equals("")) {
            Integer yearInt;
            try {
                yearInt = Integer.parseInt(yob);
            } catch (NumberFormatException e) {
                return "";
            }
            // June 1 of the year
            return Integer.toString(yearInt) + "-06-01";
        } else {
            return dob;
        }
    }

Github Code Link

Upvotes: 3

Views: 1543

Answers (2)

Vishal Tanwar
Vishal Tanwar

Reputation: 61

From the byte array you need to read values from index byte_array_length - 256 - 32 to byte_array_length - 256 to get the mobile hash in bytes(you can easily convert the bytes to hexadecimal string) and similarly for email you need to read from index byte_array_length - 256 - 32 - 32 to byte_array_length - 256 - 32.

I have implemented a library for decoding the same but the implementation is in Python.

The question is a bit old but future readers might find these resources useful.

PyPI: aadhaar-py

Github: https://github.com/vishaltanwar96/aadhaar-py

Upvotes: 1

LEGEND MORTAL
LEGEND MORTAL

Reputation: 336

Just now got the solution, fetched exact mobile and email hash value from byte array

first remove last 256 bytes from byte array then,

if only mobile present then take last 32 bytes from byte array for mobile i.e. (array.length - 32 to array.length)

else if only email present then take last 32 bytes from byte array for email i.e. (array.length - 32 to array.length)

else if both email and mobile present then take last 32 bytes from byte array for mobile, take next last 32 bytes for email i.e. (mobile = array.length - 32 to array.length and email = array.length - 64 to array.length - 32)

Upvotes: 2

Related Questions