/// <summary> /// Because the PPPv3 spec says that individual passcodes can not /// cross crypto/counter boundaries we must calculate how many /// passcodes we can generate out of a given block of 128 bits /// </summary> /// <returns></returns> public int CalculatePasscodesPerBlock(int passcodeLength, int alphabetLength) { byte[] bits128 = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; BigInteger allBits = new BigInteger(bits128); int charactersPerBlock = 0; while (allBits > alphabetLength) { allBits.ModDivInPlace(alphabetLength); charactersPerBlock++; } int codesPerBlock = charactersPerBlock / passcodeLength; return codesPerBlock; }
/// <summary> /// Calculates the passcode for the given index /// </summary> /// <param name="passcodeOrdinal"></param> /// <returns></returns> public string GetPasscode(BigInteger passcodeOrdinal) { if (passcodeOrdinal > Int128Max) throw new ArgumentOutOfRangeException("passcodeOrdinal", String.Format("Passcode Ordinals can not exceed 128 bits!", string.Empty)); byte[] crypto = GetCryptoBlockForCounter(_sequenceKey, passcodeOrdinal); StringBuilder passcode = new StringBuilder(_passcodeLength); BigInteger working = new BigInteger(crypto); while (passcode.Length < _passcodeLength) { int index = working.ModDivInPlace(_alphabet.Count); passcode.Append(_alphabet[index]); } return passcode.ToString(); }
/// <summary> /// Each passcode is generated from a seperate 128 bit crypto operation, meaning that /// passcodes can not exceed a certain length for a given alphabet. /// </summary> /// <param name="alphabetLength"></param> /// <returns></returns> public static int CalculateMaxPasscodeLength(int alphabetLength) { BigInteger allBits = new BigInteger(bits128); int blocks = 0; while (allBits > alphabetLength) { allBits.ModDivInPlace(alphabetLength); blocks++; } return blocks; }