private bool DecryptSecret(byte[] yubiResp, ChallengeInfo inf, out byte[] secret) { secret = new byte[keyLenBytes]; if (inf.IV == null) { return(false); } if (inf.Verification == null) { return(false); } //use the response to decrypt the secret SHA256 sha = SHA256Managed.Create(); byte[] key = sha.ComputeHash(yubiResp); // get a 256 bit key from the 160 bit hmac response StandardAesEngine aes = new StandardAesEngine(); using (MemoryStream msDecrypt = new MemoryStream(inf.EncryptedSecret)) { using (CryptoStream csDecrypt = (CryptoStream)aes.DecryptStream(msDecrypt, key, inf.IV)) { csDecrypt.Read(secret, 0, secret.Length); csDecrypt.Close(); } msDecrypt.Close(); } byte[] secretHash = sha.ComputeHash(secret); for (int i = 0; i < secretHash.Length; i++) { if (secretHash[i] != inf.Verification[i]) { //wrong response Array.Clear(secret, 0, secret.Length); return(false); } } //return the secret sha.Clear(); return(true); }
/// <summary> /// A method for generating encrypted ChallengeInfo to be saved. For security, this method should /// be called every time you get a successful challenge-response pair from the Yubikey. Failure to /// do so will permit password re-use attacks. /// </summary> /// <param name="secret">The un-encrypted secret</param> /// <returns>A fully populated ChallengeInfo object ready to be saved</returns> public ChallengeInfo Encrypt(byte[] secret) { //generate a random challenge for use next time byte[] challenge = GenerateChallenge(); //generate the expected HMAC-SHA1 response for the challenge based on the secret byte[] resp = GenerateResponse(challenge, secret); //use the response to encrypt the secret SHA256 sha = SHA256Managed.Create(); byte[] key = sha.ComputeHash(resp); // get a 256 bit key from the 160 bit hmac response byte[] secretHash = sha.ComputeHash(secret); StandardAesEngine aes = new StandardAesEngine(); const uint aesIVLenBytes = 16; byte[] IV = CryptoRandom.Instance.GetRandomBytes(aesIVLenBytes); byte[] encrypted; using (MemoryStream msEncrypt = new MemoryStream()) { using (CryptoStream csEncrypt = (CryptoStream)aes.EncryptStream(msEncrypt, key, IV)) { csEncrypt.Write(secret, 0, secret.Length); csEncrypt.Close(); } encrypted = msEncrypt.ToArray(); msEncrypt.Close(); } ChallengeInfo inf = new ChallengeInfo(encrypted, IV, challenge, secretHash, LT64); sha.Clear(); return(inf); }
public static ChallengeInfo Load(IOConnectionInfo ioc) { Stream sIn = null; ChallengeInfo inf = new ChallengeInfo(); try { sIn = App.Kp2a.GetOtpAuxFileStorage(ioc).OpenFileForRead(ioc); XmlSerializer xs = new XmlSerializer(typeof (ChallengeInfo)); if (!inf.LoadStream(sIn)) return null; } catch (Exception e) { Kp2aLog.Log(e.ToString()); } finally { if(sIn != null) sIn.Close(); } return inf; }
/// <summary> /// A method for generating encrypted ChallengeInfo to be saved. For security, this method should /// be called every time you get a successful challenge-response pair from the Yubikey. Failure to /// do so will permit password re-use attacks. /// </summary> /// <param name="secret">The un-encrypted secret</param> /// <returns>A fully populated ChallengeInfo object ready to be saved</returns> public ChallengeInfo Encrypt(byte[] secret) { //generate a random challenge for use next time byte[] challenge = GenerateChallenge(); //generate the expected HMAC-SHA1 response for the challenge based on the secret byte[] resp = GenerateResponse(challenge, secret); //use the response to encrypt the secret SHA256 sha = SHA256Managed.Create(); byte[] key = sha.ComputeHash(resp); // get a 256 bit key from the 160 bit hmac response byte[] secretHash = sha.ComputeHash(secret); StandardAesEngine aes = new StandardAesEngine(); const uint aesIVLenBytes = 16 ; byte[] IV = CryptoRandom.Instance.GetRandomBytes(aesIVLenBytes); byte[] encrypted; using (MemoryStream msEncrypt = new MemoryStream()) { using (CryptoStream csEncrypt = (CryptoStream)aes.EncryptStream(msEncrypt, key, IV)) { csEncrypt.Write(secret, 0, secret.Length); csEncrypt.Close(); } encrypted = msEncrypt.ToArray(); msEncrypt.Close(); } ChallengeInfo inf = new ChallengeInfo (encrypted, IV, challenge, secretHash, LT64); sha.Clear(); return inf; }
private bool DecryptSecret(byte[] yubiResp, ChallengeInfo inf, out byte[] secret) { secret = new byte[keyLenBytes]; if (inf.IV == null) return false; if (inf.Verification == null) return false; //use the response to decrypt the secret SHA256 sha = SHA256Managed.Create(); byte[] key = sha.ComputeHash(yubiResp); // get a 256 bit key from the 160 bit hmac response StandardAesEngine aes = new StandardAesEngine(); using (MemoryStream msDecrypt = new MemoryStream(inf.EncryptedSecret)) { using (CryptoStream csDecrypt = (CryptoStream)aes.DecryptStream(msDecrypt, key, inf.IV)) { csDecrypt.Read(secret, 0, secret.Length); csDecrypt.Close(); } msDecrypt.Close(); } byte[] secretHash = sha.ComputeHash(secret); for (int i = 0; i < secretHash.Length; i++) { if (secretHash[i] != inf.Verification[i]) { //wrong response Array.Clear(secret, 0, secret.Length); return false; } } //return the secret sha.Clear(); return true; }
/// <summary> /// The primary access point for challenge-response utility functions. Accepts a pre-populated ChallengeInfo object /// containing at least the IV, EncryptedSecret, and Verification fields. These fields are combined with the Yubikey response /// to decrypt and verify the secret. /// </summary> /// <param name="inf">A pre-populated object containing minimally the IV, EncryptedSecret and Verification fields. /// This should be populated from the database.xml auxilliary file</param> /// <param name="resp" >The Yubikey's response to the issued challenge</param> /// <returns>The common secret, used as a composite key to encrypt a Keepass database</returns> public byte[] GetSecret(ChallengeInfo inf, byte[] resp) { if (resp.Length != responseLenBytes) return null; if (inf == null) return null; if (inf.Challenge == null || inf.Verification == null) return null; LT64 = inf.LT64; byte[] secret; if (DecryptSecret(resp, inf, out secret)) { return secret; } else { return null; } }