An individual server side user credential
Example #1
0
        /// <summary>
        /// Requests that the provided stream be authenticated
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="additionalChallenge">Additional data to include in the challenge. If using SSL certificates,
        /// adding the thumbprint to the challenge will allow detecting man in the middle attacks.</param>
        /// <returns></returns>
        public SrpServerSession AuthenticateAsServer(Stream stream, byte[] additionalChallenge = null)
        {
            if (additionalChallenge is null)
            {
                additionalChallenge = new byte[] { }
            }
            ;

            // Header
            //  C => S
            //  int16   usernameLength (max 1024 characters)
            //  byte[]  usernameBytes

            int len = stream.ReadInt16();

            if (len < 0 || len > 1024)
            {
                return(null);
            }

            byte[]            usernameBytes = stream.ReadBytes(len);
            string            username      = UTF8.GetString(usernameBytes);
            SrpUserCredential user          = Users.Lookup(username);
            SrpServerSession  session       = new SrpServerSession(user);

            if (session.TryAuthenticate(stream, additionalChallenge))
            {
                return(session);
            }
            return(null);
        }
    }
 /// <summary>
 /// Adds the specified user to the credential database.
 /// </summary>
 /// <param name="username"></param>
 /// <param name="verifier"></param>
 /// <param name="passwordSalt"></param>
 /// <param name="iterations"></param>
 /// <param name="strength"></param>
 public void AddUser(string username, byte[] verifier, byte[] passwordSalt, int iterations, SrpStrength strength)
 {
     var user = new SrpUserCredential(username, passwordSalt, verifier, iterations, strength);
     lock (m_users)
     {
         m_users.Add(username, user);
     }
 }
 /// <summary>
 /// Adds the specified user to the credentials database.
 /// </summary>
 /// <param name="username"></param>
 /// <param name="password"></param>
 /// <param name="strength"></param>
 /// <param name="saltSize"></param>
 /// <param name="iterations"></param>
 public void AddUser(string username, string password, SrpStrength strength = SrpStrength.Bits1024, int saltSize = 32, int iterations = 4000)
 {
     var user = new SrpUserCredential(username, password, strength, saltSize, iterations);
     lock (m_users)
     {
         m_users.Add(username, user);
     }
 }
        /// <summary>
        /// Adds the specified user to the credential database.
        /// </summary>
        /// <param name="username"></param>
        /// <param name="verifier"></param>
        /// <param name="passwordSalt"></param>
        /// <param name="iterations"></param>
        /// <param name="strength"></param>
        public void AddUser(string username, byte[] verifier, byte[] passwordSalt, int iterations, SrpStrength strength)
        {
            SrpUserCredential user = new SrpUserCredential(username, passwordSalt, verifier, iterations, strength);

            lock (m_users)
            {
                m_users.Add(username, user);
            }
        }
        /// <summary>
        /// Adds the specified user to the credentials database.
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="strength"></param>
        /// <param name="saltSize"></param>
        /// <param name="iterations"></param>
        public void AddUser(string username, string password, SrpStrength strength = SrpStrength.Bits1024, int saltSize = 32, int iterations = 4000)
        {
            SrpUserCredential user = new SrpUserCredential(username, password, strength, saltSize, iterations);

            lock (m_users)
            {
                m_users.Add(username, user);
            }
        }
Example #6
0
        private static unsafe byte[] CreateSessionData(byte[] sessionSecret, SrpUserCredential user)
        {
            byte[] initializationVector = SaltGenerator.Create(16);
            int    len      = sessionSecret.Length;
            int    blockLen = (len + 15) & ~15; //Add 15, then round down. (Effectively rounds up to the nearest 128 bit boundary).

            byte[] dataToEncrypt = new byte[blockLen];
            sessionSecret.CopyTo(dataToEncrypt, 0);

            //fill the remainder of the block with random bits
            if (len != blockLen)
            {
                SaltGenerator.Create(blockLen - len).CopyTo(dataToEncrypt, len);
            }

            byte[] ticket = new byte[1 + 16 + 8 + 16 + 2 + blockLen + 32];

            using (Aes aes = Cipher.CreateAes())
            {
                aes.Key     = user.ServerEncryptionkey;
                aes.IV      = initializationVector;
                aes.Mode    = CipherMode.CBC;
                aes.Padding = PaddingMode.None;
                ICryptoTransform decrypt       = aes.CreateEncryptor();
                byte[]           encryptedData = decrypt.TransformFinalBlock(dataToEncrypt, 0, dataToEncrypt.Length);

                fixed(byte *lp = ticket)
                {
                    using (BinaryStreamPointerWrapper stream = new BinaryStreamPointerWrapper(lp, ticket.Length))
                    {
                        stream.Write((byte)1);
                        stream.Write((short)len);
                        stream.Write(user.ServerKeyName);
                        stream.Write(DateTime.UtcNow);
                        stream.Write(initializationVector);
                        stream.Write(encryptedData);
                        stream.Write(HMAC <Sha256Digest> .Compute(user.ServerHMACKey, ticket, 0, ticket.Length - 32));
                    }
                }
            }
            return(ticket);
        }
 /// <summary>
 /// Creates a new <see cref="SrpServerSession"/> that will authenticate a stream.
 /// </summary>
 /// <param name="user">The user that will be authenticated.</param>
 public SrpServerSession(SrpUserCredential user)
 {
     m_user = user;
 }
        /// <summary>
        /// Attempts to load the session resume ticket.
        /// </summary>
        /// <param name="ticket">The serialized data for the key</param>
        /// <param name="user">The user's credentials so the proper encryption key can be used</param>
        /// <param name="sessionSecret">the session secret decoded if successful. null otherwise</param>
        /// <returns>
        /// True if the ticket is authentic
        /// </returns>
        unsafe static bool TryLoadTicket(byte[] ticket, SrpUserCredential user, out byte[] sessionSecret)
        {
            sessionSecret = null;

            //Ticket Structure:
            //  byte      TicketVersion = 1
            //  int16     Username Length
            //  int16     Session Key Length 
            //  Guid      ServerKeyName
            //  DateTime  AuthenticationTime
            //  byte[16]  IV
            //  byte[]    Username
            //  byte[]    Encrypted Session Data (Round [Session Key Length] up to a 128 bit number to get the length of the encrypted data)
            //  byte[32]  HMAC (Sha2-256)

            if (ticket == null || ticket.Length < 1 + 16 + 8 + 16 + 2 + 32)
                return false;

            fixed (byte* lp = ticket)
            {
                var stream = new BinaryStreamPointerWrapper(lp, ticket.Length);
                if (stream.ReadUInt8() != 1)
                    return false;

                int sessionKeyLength = stream.ReadUInt16();
                if (sessionKeyLength < 0 || sessionKeyLength > 1024) //Max session key is 8192 SRP.
                    return false;

                int encryptedDataLength = (sessionKeyLength + 15) & ~15; //Add 15, then round down. (Effecitvely rounds up to the nearest 128 bit boundary).
                if (ticket.Length != 1 + 2 + 16 + 8 + 16 + encryptedDataLength + 32)
                    return false;

                if (!user.ServerKeyName.SecureEquals(stream.ReadGuid()))
                    return false;

                long maxTicketAge = DateTime.UtcNow.Ticks + TimeSpan.TicksPerMinute * 10; //Allows for time syncing issues that might move the server's time back by a few minutes.
                long minTicketAge = maxTicketAge - TimeSpan.TicksPerDay;

                long issueTime = stream.ReadInt64();
                if (issueTime < minTicketAge || issueTime > maxTicketAge)
                    return false;

                byte[] initializationVector = stream.ReadBytes(16);

                //Verify the signature if everything else looks good.
                //This is last because it is the most computationally complex.
                //This limits denial of service attackes.
                byte[] hmac = HMAC<Sha256Digest>.Compute(user.ServerHMACKey, ticket, 0, ticket.Length - 32);
                if (!hmac.SecureEquals(ticket, ticket.Length - 32, 32))
                    return false;

                byte[] encryptedData = stream.ReadBytes(encryptedDataLength);
                var aes = new RijndaelManaged();
                aes.Key = user.ServerEncryptionkey;
                aes.IV = initializationVector;
                aes.Mode = CipherMode.CBC;
                aes.Padding = PaddingMode.None;
                var decrypt = aes.CreateDecryptor();
                sessionSecret = decrypt.TransformFinalBlock(encryptedData, 0, encryptedData.Length);

                return true;
            }
        }
        static unsafe byte[] CreateSessionData(byte[] sessionSecret, SrpUserCredential user)
        {
            byte[] initializationVector = SaltGenerator.Create(16);
            int len = sessionSecret.Length;
            int blockLen = (len + 15) & ~15; //Add 15, then round down. (Effecitvely rounds up to the nearest 128 bit boundary).
            byte[] dataToEncrypt = new byte[blockLen];
            sessionSecret.CopyTo(dataToEncrypt, 0);

            //fill the remainder of the block with random bits
            if (len != blockLen)
            {
                SaltGenerator.Create(blockLen - len).CopyTo(dataToEncrypt, len);
            }

            byte[] ticket = new byte[1 + 16 + 8 + 16 + 2 + blockLen + 32];

            var aes = new RijndaelManaged();
            aes.Key = user.ServerEncryptionkey;
            aes.IV = initializationVector;
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.None;
            var decrypt = aes.CreateEncryptor();
            var encryptedData = decrypt.TransformFinalBlock(dataToEncrypt, 0, dataToEncrypt.Length);

            fixed (byte* lp = ticket)
            {
                var stream = new BinaryStreamPointerWrapper(lp, ticket.Length);
                stream.Write((byte)1);
                stream.Write((short)len);
                stream.Write(user.ServerKeyName);
                stream.Write(DateTime.UtcNow);
                stream.Write(initializationVector);
                stream.Write(encryptedData);
                stream.Write(HMAC<Sha256Digest>.Compute(user.ServerHMACKey, ticket, 0, ticket.Length - 32));
            }
            return ticket;
        }
Example #10
0
 /// <summary>
 /// Creates a new <see cref="SrpServerSession"/> that will authenticate a stream.
 /// </summary>
 /// <param name="user">The user that will be authenticated.</param>
 public SrpServerSession(SrpUserCredential user)
 {
     m_user = user;
 }
Example #11
0
        /// <summary>
        /// Attempts to load the session resume ticket.
        /// </summary>
        /// <param name="ticket">The serialized data for the key</param>
        /// <param name="user">The user's credentials so the proper encryption key can be used</param>
        /// <param name="sessionSecret">the session secret decoded if successful. null otherwise</param>
        /// <returns>
        /// True if the ticket is authentic
        /// </returns>
        unsafe static bool TryLoadTicket(byte[] ticket, SrpUserCredential user, out byte[] sessionSecret)
        {
            sessionSecret = null;

            //Ticket Structure:
            //  byte      TicketVersion = 1
            //  int16     Username Length
            //  int16     Session Key Length
            //  Guid      ServerKeyName
            //  DateTime  AuthenticationTime
            //  byte[16]  IV
            //  byte[]    Username
            //  byte[]    Encrypted Session Data (Round [Session Key Length] up to a 128 bit number to get the length of the encrypted data)
            //  byte[32]  HMAC (Sha2-256)

            if (ticket == null || ticket.Length < 1 + 16 + 8 + 16 + 2 + 32)
                return(false);

            fixed(byte *lp = ticket)
            {
                var stream = new BinaryStreamPointerWrapper(lp, ticket.Length);

                if (stream.ReadUInt8() != 1)
                {
                    return(false);
                }

                int sessionKeyLength = stream.ReadUInt16();

                if (sessionKeyLength < 0 || sessionKeyLength > 1024) //Max session key is 8192 SRP.
                {
                    return(false);
                }

                int encryptedDataLength = (sessionKeyLength + 15) & ~15; //Add 15, then round down. (Effecitvely rounds up to the nearest 128 bit boundary).

                if (ticket.Length != 1 + 2 + 16 + 8 + 16 + encryptedDataLength + 32)
                {
                    return(false);
                }

                if (!user.ServerKeyName.SecureEquals(stream.ReadGuid()))
                {
                    return(false);
                }

                long maxTicketAge = DateTime.UtcNow.Ticks + TimeSpan.TicksPerMinute * 10; //Allows for time syncing issues that might move the server's time back by a few minutes.
                long minTicketAge = maxTicketAge - TimeSpan.TicksPerDay;

                long issueTime = stream.ReadInt64();

                if (issueTime < minTicketAge || issueTime > maxTicketAge)
                {
                    return(false);
                }

                byte[] initializationVector = stream.ReadBytes(16);

                //Verify the signature if everything else looks good.
                //This is last because it is the most computationally complex.
                //This limits denial of service attackes.
                byte[] hmac = HMAC <Sha256Digest> .Compute(user.ServerHMACKey, ticket, 0, ticket.Length - 32);

                if (!hmac.SecureEquals(ticket, ticket.Length - 32, 32))
                {
                    return(false);
                }

                byte[] encryptedData = stream.ReadBytes(encryptedDataLength);
                var    aes           = new RijndaelManaged();

                aes.Key     = user.ServerEncryptionkey;
                aes.IV      = initializationVector;
                aes.Mode    = CipherMode.CBC;
                aes.Padding = PaddingMode.None;
                var decrypt = aes.CreateDecryptor();

                sessionSecret = decrypt.TransformFinalBlock(encryptedData, 0, encryptedData.Length);

                return(true);
            }
        }