Home > database >  Checking a password meets a specific format in C
Checking a password meets a specific format in C

Time:03-30

Im creating a password checker in C. The user enters their password and the program must check it fits the format aa$B1BB$B$$aB (where a represents lowercase, B uppercase, $ symbol and 1 is a digit).

I have currently programmed an array of numbers 0 to 9, symbols and letters and am trying to create a series of iterative loops to search each character of the password against the matching array of the criteria that it should meet. This was the easiest way I thought of but if anyone has any better suggestions it would be much appreciated.

The errors I am currently facing is that the program isn't running as I expected. If the password is not 13 characters long it should call the else if statement but it doesn't. So far I have only created statements for checking digits 1, 2, 3 and 4. The printf("Password ok #") statements are just for testing purposes.

#include <string.h>
#include <stdio.h>

int main() {
    char password[20];
    int i, length;
    int numbers[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    char letters[] = { 'a', 'b', 'c', 'c', 'e', 'f', 'g', 'h',
                       'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
                       'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
                       'y', 'z' };
    char symbols[] = { '!', '@', '#', '$', '%', '^', '&', '*', 
                       '-', '_', ' ', '=' };

    printf("Password rules:\n"
           " --------------------- \n"
           " must follow the format ee$L1LL$L$$eL \n"
           " --------------------- \n"
           " Please enter your current password:");
    scanf("%c", password);
    printf("s", password);

    length = strlen(password);

    if (length = 13) {
        printf("Password ok 1");
        for (int i = 0; i < strlen(letters); i  ) {    
            if ((password[1] == letters[i]) && (password[2] == letters[i])) {
                printf("Password ok 2");
            } else {
                printf("Digits 1 or 2 do not fit the correct format");
            }
        }
        for (int i = 0; i < strlen(symbols); i  ) {
            if (password[3] == symbols[i]) {
                printf("Password ok 3");
            } else {
                printf("Digit 3 does not fit the correct format");
            }
        }
        for (int i = 0; i < strlen(letters); i  ) {
            if (password[4]==symbols[i]) {
                printf("Password ok 4");
            } else {
                printf("Digit 3 does not fit the correct format");
            }
        }
    } else if (length != 13) {
        printf("\nPassword must be 13 digits long");
    }
}

CodePudding user response:

To check if the password fits

format aa$B1BB$B$$aB

Use fgets() to read, sscanf() to scan and "%n" to detect scanning end.

// scanf("%c",password); // %c only reads 1 character
// printf("s",password);

if (fgets(password, sizeof password, stdin)) {
  password[strcspn(password, "\n")] = '\0'; // Lop off potential \n
  printf("<%s>\n", password);
  int n = 0; 
  // format aa$B1BB$B$$aB
  #define FMT_a "%*1[a-z]"
  #define FMT_B "%*1[A-Z]"
  #define FMT_1 "%*1[0-9]"
  #define FMT_S "%*1[-!@#$%^&*_ =]"  // '-' first
  sscanf(password, 
      FMT_a FMT_a FMT_S FMT_B FMT_1 FMT_B FMT_B FMT_S FMT_B FMT_S FMT_S FMT_a FMT_B "%n",
      &n);
  bool Success = n > 0 && password[n] == '\0';

A more robust approach uses a series of strchr() calls in case letters [a-z] are not consecutive.


IMO, use a more generous read buffer.

// char password[20];
char password[100];

CodePudding user response:

If you're allowed to use ctype macros like isupper(), islower() then you can call them against every pattern-char as.

#include <stdio.h>
#include <ctype.h>
#include <string.h>

enum { // Enumerate codes used in the password-pattern
    eLower = 'a',
    eUpper = 'B',
    eDigit = '1',
    eSymbol = '$'
};

char* pp_errors[] = { // Error strings for pattern-mismatch
    "No Error",
    "Lower case letter expected",
    "Upper case letter expected",
    "Digit is expected",
    "Punctuation expected",
    "Pattern Not handled"
};

int
check_pattern_char (const unsigned char pwdC, const unsigned char patC)
{
    switch (patC) {
    case eLower : if (!islower (pwdC)) return 1;
        break;
    case eUpper : if (!isupper (pwdC)) return 2;
        break;
    case eDigit : if (!isdigit (pwdC)) return 3;
        break;
    case eSymbol : if (!ispunct (pwdC)) return 4;
        break;
    default: return 5; // pattern not handled
    }
    return 0;
}

int main ()
{
    char password [128];
    char pattern [] = "aa$B1BB$B$$aB";
    int ptlen = strlen (pattern);

    printf ("Enter a password that follows pattern [%s]: ", pattern);
    while (1 != scanf ("7s", password));
    if ( (int) strlen (password) != ptlen) {
        printf ("\nPassword must be [%d] chars long.\n", ptlen);
        return 1;
    }
    for (int pi = 0; pi < ptlen;   pi) {
        int status;
        if ( (status = check_pattern_char (password[pi], pattern[pi]))) {
            printf ("\nERROR: [%s] at pos[%d] [%c][%c]\n",
                    pp_errors[status], pi   1, pattern[pi], password[pi]);
            return 2;
        }
    }
    printf ("\nPassword [%s] accepted against Pattern[%s]\n", password, pattern);
    return 0;
}

Or you can write your own functions for them like :

static inline int my_islower (const char ch) {
    return (ch >= 'a' && ch <= 'z');
}
static inline int my_isupper (const char ch) {
    return (ch >= 'A' && ch <= 'Z');
}
static inline int my_isdigit (const char ch) {
    return (ch >= '0' && ch <= '9');
}
static inline int my_ispunct (const char ch) {
    // allowed punctuations
    char puncts[] = "!@#$%^&*-_ =";
    for (char* pp = puncts; *pp; )
        if (*pp   == ch) return 1;
    return 0;
}

static inline is telling compiler that this function is local to this translation-unit(current file) & requesting it to inline function if possible at the calling location. This mitigates the overheads of calling small functions.

  •  Tags:  
  • c
  • Related