* Rules to get a password from this document: http://www.csb.gc.ca/eng/pdf/ftps.pdf * * Rules are: * The FTPS server performs the following validations on the password before accepting it: * 1 · The password must be ASCII character encoded text. * 2 · The password must be at least 12 characters long but no more than 40 characters long. * 3 · The password must contain at least one lower-case alphabetic character, one uppercase * alphabetic character, and two numeric characters. * 4 · The password must contain only a combination of the following characters: ‘a’ to ‘z’, * ‘A’ to ‘Z’, and ‘0’ to ‘9’. * 5 · The first eight characters of the password must contain at least one numeric character * and two alphabetic characters. * 6 · The password cannot be a circular shift of the user id (note that such a password * would be invalid anyway because it would be only nine characters long and therefore * too short). * 7 · The new password must differ from the previous password and cannot be a reverse or * circular shift of the previous password. For this comparison, uppercase letters and * lowercase letters are considered to be equal (Not implemented). * 8 · The new password must have at least three characters that are different from the old * password. For this comparison, uppercase letters and lowercase letters are considered * to be equal.k #define MAX_LENGTH 40 && Rule 2 #define MIN_LENGTH 12 && Rule 2 #define MIN_LOWERCASE 1 && Rule 3 #define MIN_UPPERCASE 1 && Rule 3 #define MIN_DIGITS 2 && Rule 3 #define ROOT_LENGTH 8 && Rule 5 #define ROOT_ALPHACOUNT 2 && Rule 5 #define ROOT_DIGITSCOUNT 2 && Rule 5 #define ALL_DIGITS '0123456789' #define ALL_LETTERS 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' #define MIN_DIFFCHARCOUNT 3 local lnLength loRPG = Createobject('RandomPasswordGenerator') clear *? 'Len' + Chr(9) + 'Digits' + Chr(9) + 'Lower' + Chr(9) + 'Upper' + Chr(9) + 'Root N' + Chr(9) + 'Root Char' + Chr(13)+Chr(10) for i =1 to 25 lcPassword = loRPG.newPassword() lcError = '' llPasswordOK = loRPG.CheckPassword(lcPassword, @lcError) if llPasswordOK ? lcPassword, 'OK' else Messagebox(lcError, 16, 'Invalid password: ' + lcPassword) endif next i lcError = '' lcPassword = Inputbox('Enter the password') llPasswordOK = loRPG.CheckPassword(lcPassword, '', @lcError) if llPasswordOK ? lcPassword, 'OK' else Messagebox(lcError, 16, 'Invalid password: ' + lcPassword) endif return define class RandomPasswordGenerator as Session protected Random as Object function Init() as Boolean this.Random = Newobject('RandomFunctions') return .t. endfunc procedure Destroy() as VOID this.Random = null return null endproc function newPassword() as String local lnLength, lnLowerCaseCount, lnUpperCaseCount, lnDigitsCount, lnRootNumCount, lnRootCharCount local lcPassword, lcRoot lnLength = this.Random.getRandomInteger(MIN_LENGTH, MAX_LENGTH) lnDigitsCount = this.Random.getRandomInteger(MIN_DIGITS, lnLength - MIN_UPPERCASE - MIN_LOWERCASE) lnLowerCaseCount = this.Random.getRandomInteger(MIN_LOWERCASE, lnLength - lnDigitsCount - MIN_UPPERCASE) lnUpperCaseCount = lnLength - lnDigitsCount - lnLowerCaseCount lnRootNumCount = this.Random.getRandomInteger(ROOT_DIGITSCOUNT, Min(lnDigitsCount, ROOT_LENGTH - ROOT_DIGITSCOUNT)) lnRootCharCount = ROOT_LENGTH - lnRootNumCount * Get the first 8 Characters, first the digits then the chars, later this will be shuffled lcUnshuffledRoot = '' * Getting the digits for lnPosition = 1 to lnRootNumCount lcUnshuffledRoot = lcUnshuffledRoot + this.Random.getRandomDigit() next lnPosition * Getting the chars, we need to keep a count on how many UpperCase and LowerCase chars are used for lnPosition = 1 to lnRootCharCount do case case lnUpperCaseCount > 0 and lnLowerCaseCount > 0 lcChar = this.Random.getRandomLetter() case lnUpperCaseCount > 0 lcChar = this.Random.getRandomUpperCase() case lnLowerCaseCount > 0 lcChar = this.Random.getRandomLowerCase() otherwise **** OOOOPSSS. Take a deep breath and think about this. endcase lcUnshuffledRoot = lcUnshuffledRoot + lcChar if Isupper(lcChar) lnUpperCaseCount = lnUpperCaseCount - 1 else lnLowerCaseCount = lnLowerCaseCount - 1 endif next lnPosition lcRoot = this.Random.StringShuffle(lcUnshuffledRoot) lnDigitsCount = lnDigitsCount - lnRootNumCount lcPassword = '' for lnPosition = 1 to lnDigitsCount lcPassword = lcPassword + this.Random.getRandomDigit() next lnPosition for lnPosition = 1 to lnUpperCaseCount lcPassword = lcPassword + this.Random.getRandomUpperCase() next lnPosition for lnPosition = 1 to lnLowerCaseCount lcPassword = lcPassword + this.Random.getRandomLowerCase() next lnPosition return lcRoot + this.Random.StringShuffle(lcPassword) endfunc function CheckLength(tcPassword as String) as Boolean return Between(Len(tcPassword), MIN_LENGTH, MAX_LENGTH) endfunc function CheckValididyOfChars(tcPassword as String) as Boolean local lcInvalidCharacters lcInvalidCharacters = Chrtran(Upper(tcPassword), ALL_DIGITS + ALL_LETTERS, '') return Len(lcInvalidCharacters) = 0 endfunc function CheckEnoughStartingDigits(tcPasswordRoot as String) as Boolean local lnDigitsRoot lnDigitsRoot = Len(tcPasswordRoot) - Len(Chrtran(tcPasswordRoot, ALL_DIGITS, '')) return lnDigitsRoot >= ROOT_DIGITSCOUNT endfunc function CheckEnoughStartingLetters(tcPasswordRoot as String) as Boolean local lnLettersRoot lnLettersRoot = Len(tcPasswordRoot) - Len(Chrtran(tcPasswordRoot, ALL_DIGITS, '')) return lnLettersRoot >= ROOT_ALPHACOUNT endfunc function CheckEnoughDigits(tcPassword as String) as Boolean local lnDigits lnDigits = Len(tcPassword) - Len(Chrtran(tcPassword, ALL_DIGITS, '')) return lnDigits >= MIN_DIGITS endfunc function CheckEnoughLowerCaseLetters(tcPassword as String) as Boolean local lnTotalLowerCase lnTotalLowerCase = Len(tcPassword) - Len(Chrtran(tcPassword, Lower(ALL_LETTERS), '')) return lnTotalLowerCase >= MIN_LOWERCASE endfunc function CheckEnoughUpperCaseLetters(tcPassword as String) as Boolean local lnTotalUpperCase lnTotalUpperCase = Len(tcPassword) - Len(Chrtran(tcPassword, ALL_LETTERS, '')) return lnTotalUpperCase >= MIN_UPPERCASE endfunc function CheckDifferentCharactersCount(tcCurrentPassword as String, tcPreviousPassword as String) as Integer return Len(Chrtran(Upper(tcCurrentPassword), Upper(tcPreviousPassword), '')) endfunc function CheckPassword(tcPassword as String, tcPreviousPassword, tcResult as String) as Boolean local lcRoot, lcResult, llSetResult, lcInvalidCharacters, lcPreviousPassword, lcNewCharacters llSetResult = Pcount() = 3 lcResult = '' lcPreviousPassword = Iif(Vartype(tcPreviousPassword) = 'C', tcPreviousPassword, '') ASSERT .f. * Check the length if not Between(Len(tcPassword), MIN_LENGTH, MAX_LENGTH) lcResult = 'Invalid Length. Current value: ' + Transform(Len(tcPassword)) endif lcInvalidCharacters = Chrtran(tcPassword, ALL_DIGITS + ALL_LETTERS + Lower(ALL_LETTERS), '') if Len(lcInvalidCharacters) # 0 lcResult = lcResult + Iif(Empty(lcResult), '', Chr(13)) + 'The password has invalid characters: [' + lcInvalidCharacters + ']' endif lcRoot = Left(tcPassword, ROOT_LENGTH) lnDigitsRoot = Len(lcRoot) - Len(Chrtran(lcRoot, ALL_DIGITS, '')) lnUpperCRoot = Len(lcRoot) - Len(Chrtran(lcRoot, ALL_LETTERS, '')) - lnDigitsRoot lnLowerCRoot = Len(lcRoot) - lnDigitsRoot - lnUpperCRoot * At least two MIN_DIGITS digits at the beginning if lnDigitsRoot < MIN_DIGITS lcResult = lcResult + Iif(Empty(lcResult), '', Chr(13)) + 'Insuficient digits at start: ' + Transform(lnDigitsRoot) endif * At least two ROOT_ALPHACOUNT chars within the firt ROOT_ALPHACOUNT characters if lnUpperCRoot + lnLowerCRoot < ROOT_ALPHACOUNT lcResult = lcResult + Iif(Empty(lcResult), '', Chr(13)) + 'Insuficient letters at start: ' + Transform(lnUpperCRoot + lnLowerCRoot) endif * At Lest MIN_LOWERCASE lower case char lnTotalLowerCase = Len(tcPassword) - Len(Chrtran(tcPassword, Lower(ALL_LETTERS), '')) if lnTotalLowerCase < MIN_LOWERCASE lcResult = lcResult + Iif(Empty(lcResult), '', Chr(13)) + 'Insuficient lower case letters: ' + Transform(lnTotalLowerCase) endif * At Lest MIN_UPPERCASE lower case char lnTotalUpperCase = Len(tcPassword) - Len(Chrtran(tcPassword, ALL_LETTERS, '')) if lnTotalUpperCase < MIN_UPPERCASE lcResult = lcResult + Iif(Empty(lcResult), '', Chr(13)) + 'Insuficient upper case letters: ' + Transform(lnTotalUpperCase) endif * Check how many characters are different lcNewCharacters = Chrtran(Upper(tcPassword), Upper(lcPreviousPassword), '') if Len(lcNewCharacters) < MIN_DIFFCHARCOUNT lcResult = lcResult + Iif(Empty(lcResult), '', Chr(13)) + 'Insuficient new characters.' + Chr(13) + Chr(9) + 'Try using some from this group: [' + Chrtran(ALL_DIGITS + ALL_LETTERS + Lower(ALL_LETTERS), lcPreviousPassword, '') + ']' endif if llSetResult tcResult = lcResult endif return Empty(lcResult) endfunc enddefine ******************************************************************************************************************* * * * Collection of functions to return random numbers or letters * * * * Methods: * * * * ReSeed - Set a new seed for the internal random number generator * * getRandomDigit - Returns a random digit as character ('0'..'9') * * getRandomLowerCase - Returns a random lower case letter ('a'..'z') * * getRandomUpperCase - Returns a random upper case letter ('A'..'Z') * * getRandomLetter - Returns a random upper or lower case letter ('A'..'Z'|'a'..'z') * * * * * ******************************************************************************************************************* define class RandomFunctions as Session function init() as Boolean Rand(-1) return .t. endfunc procedure ReSeed(tnNewSeed as Number) as VOID Rand(Iif(Vartype(tnNewSeed) = 'N', tnNewSeed, -1)) return null endproc function getRandomDigit() as Character return this.getRandomAscii(Asc('0'), Asc('9')) endfunc function getRandomLowerCase() as Character return this.getRandomAscii(Asc('a'), Asc('z')) endfunc function getRandomUpperCase() as Character return this.getRandomAscii(Asc('A'), Asc('Z')) endfunc function getRandomLetter() as Character return Iif(Rand() < 0.5, this.getRandomUpperCase(), this.getRandomLowerCase()) endfunc function getRandomNumber() as Number return Rand() endfunc function getRandomInteger(tnLowerLimit as Integer, tnUpperLimit as Integer) as Integer local lnLowerLimit, lnUpperLimit, lnAux lnLowerLimit = Iif(Vartype(tnLowerLimit) # 'N' or Vartype(tnUpperLimit) # 'N', 0, Int(tnLowerLimit)) lnUpperLimit = Iif(Vartype(tnUpperLimit) # 'N', Iif(Vartype(tnLowerLimit) # 'N', 1, Int(tnLowerLimit)), Int(tnUpperLimit)) if lnUpperLimit < lnLowerLimit lnAux = lnLowerLimit lnLowerLimit = lnUpperLimit lnUpperLimit = lnAux endif * Default will be getRandomInteger(0, 1) * getRandom(5) = getRandomInteger(0, 5) return Int(Rand() * (lnUpperLimit - lnLowerLimit + 1)) + lnLowerLimit endfunc function getRandomAscii(tnLowerLimit as Integer, tnUpperLimit as Integer) as Character local lnLowerLimit, lnUpperLimit * Default will be getRandomAscii(0, 255) lnLowerLimit = Iif(Vartype(tnLowerLimit) # 'N' or Vartype(tnUpperLimit) # 'N', 0, Int(tnLowerLimit)) lnUpperLimit = Iif(Vartype(tnUpperLimit) # 'N', Iif(Vartype(tnLowerLimit) # 'N', 255, Int(tnLowerLimit)), Int(tnUpperLimit)) return Chr(this.getRandomInteger(lnLowerLimit, lnUpperLimit)) endfunc * http://en.wikipedia.org/wiki/Fisher-Yates_shuffle function StringShuffle(tcUnshuffled as String) as String local lnChars, lnChar, lcShuffled, lnPosition, lnRandomChar, lcTempChar lnChars = Len(tcUnshuffled) lcShuffled = tcUnshuffled for lnChar = lnChars to 1 step -1 lnPosition = this.getRandomInteger(1, lnChar) lcRandomChar = Substr(lcShuffled, lnPosition, 1) lcTempChar = Substr(lcShuffled, lnChar, 1) lcShuffled = Stuff(lcShuffled, lnChar, 1, lcRandomChar) lcShuffled = Stuff(lcShuffled, lnPosition, 1, lcTempChar) next lnChar return lcShuffled endfunc enddefine