Friday, October 24, 2014

Do you like bananas? (but what does that have to do with passwords?)

In researching password complexity while I plan to spin up a new SharePoint 2013 farm, I came across this particular gem in my searches:

"Take five chimpanzees. Put them in a big cage. Suspend some bananas from the roof of the cage. Provide the chimpanzees with a stepladder. BUT also add a proximity detector to the bananas, so that when a chimp goes near the banana, water hoses are triggered and the whole cage is thoroughly soaked.
Soon, the chimps learn that the bananas and the stepladder are best ignored.
Now, remove one chimp, and replace it with a fresh one. That chimp knows nothing of the hoses. He sees the banana, notices the stepladder, and because he is a smart primate, he envisions himself stepping on the stepladder to reach the bananas. He then deftly grabs the stepladder... and the four other chimps spring on him and beat him squarely. He soon learns to ignore the stepladder.
Then, remove another chimp and replace it with a fresh one. The scenario occurs again; when he grabs the stepladder, he gets mauled by the four other chimps -- yes, including the previous "fresh" chimp. He has integrated the notion of "thou shallt not touch the stepladder". [sic]

Iterate. After some operations, you have five chimps who are ready to punch any chimp who would dare touch the stepladder -- and none of them knows why.

Originally, some developer, somewhere, was working on an old Unix system from the previous century, which used the old DES-based "crypt", [original link preserved] actually a password hashing function derived from the DES block cipher. In that hashing function, only the first eight characters of the password are used (and only the low 7 bits of each character, as well). Subsequent characters are ignored. That's the banana.
The Internet is full of chimpanzees."

[source/sauce: What technical reasons are there to have low maximum password lengths?]


Saturday, August 2, 2014

Stupid things can lead to wet basements... A Cautionary Tale

In a whirlwind of bad luck, my condensate pump went up on the furnace about a year after we moved into the house.  It's an easy part to replace:  some tubing for the pump discharge (reuse what's there), some PVC for the inlet for the furnace (some basic plumbing), a 110V outlet for the pump (provided by code on the furnace itself, so just reuse it), and 2 wires that tell the AC to stop because the pump is full (this is supposed to prevent basement flooding, and is the basis for this hack).
The replacement pump I bought turned out to be crap but I was lucky in the sense that the overflow float switch on that unit was wired the same way as on the old one, so it was almost a drop-in replacement (the plumbing part came in as I had to move the pump's placement on the wall, meaning the fill tube from the AC had to be rerouted).  However, when I went to replace that dud with a "better" pump, things went awry...
I had to reroute the plumbing a SECOND time, forcing me to start from scratch with all new pipes (damn PVC glue...) and to incorporate some vinyl tubing to make placement this time that much easier.  But worst of all, the switch on the "better" pump is Normally Open (the switch closes when the water level is at overflow).  On the previous two pumps, the switch was Normally Closed (the connection would go open circuit if the water was at overflow levels).  Not having the ability to switch the switch, as this "better" pump housing is completely sealed with only inlet vents for the water, the outlet pipe for the discharge, the 2 wires for the overflow switch, and the power cord, I was stuck.  NOTHING is accessible to change that overflow switch.  So I thought "well it's a new pump, so until I can fix it, I'll jumper the wires on the furnace and hope for the best."
Shouldn't have said that... I should not have said that.
continued...

Thursday, May 22, 2014

OPEN LETTER TO THE FCC

(sent to openinternet@fcc.gov, a mailbox specifically for public comments about Net Neutrality)

This e-mail will become part of the permanent public comment record at the FCC.  If you plan on writing them also, be warned you are making PUBLIC COMMENTS VISIBLE TO EVERYBODY.  In my case, the more that see this, the better.

UPDATE:  MAY 27, 2014  A RESPONSE FROM THE CHAIRMAN!  (pasted at the bottom)

"To Whom It May Concern regarding Net Neutrality (and the so called "Fast Lane") and the regulation of ISP's as Common Carriers

The concept of Internet Service Providers charging Content Providers additional fees to allow their content to flow through the ISP's networks (without impairment) while also charging end-customers high prices to gain access to ANY content (regardless of provider) is askew of any truly free-market model.

Wednesday, November 27, 2013

Why Google Needs to Update Their Two-Factor Authenticator/TOTP System

BACKGROUND

So those of us that are security and hacker conscientious use two-factor authentication for our online and work accounts whenever possible. 

Basically two-factor authentication is the concept that your User Name or ID is something you are (which you have to prove), your Password is something that you know (the first factor of proving you are who you say you are), and something that you Have (the second factor of proof).  So for example, your cell phone that can receive a text-message or an e-mail generated by the system with a unique one-time code when you try to log in with a user name and password is a two-factor system. 

Another example is a system where the server and the user know a shared secret (other than the password).  This secret was established during user enrollment, and is used, along with a known, shared counter, or some kind of time-code, to produce a PIN that is only usable once, and thus changes with each log-in attempt. 

Google uses this second system.  Twitter and Yahoo use the first type of system.  Microsoft offers a two-factor system but I haven't played with it yet.

GOOGLE

Google uses the open standard TOTP (RFC 6238), which is nice because everyone knows exactly what it is, how it works, and how secure it is because everything is transparent like you would want in a Crypto based system.  They developed their own app which is available for all major mobile devices (but no desktop app; author’s plug: I wrote one myself in .NET, it’s available HERE at CodePlex).  This produces 6-digit codes every 30 seconds, according to the default assumptions in the standard.

When you sign up for the two-factor system, they provide a barcode that is basically a URI string, defining the properties of the account, which you can scan with your phone (or a simple Base32 string you can type into a device that has the app but no camera).  They also provide 10, 8-digit “emergency” codes that are one-time use in case you need to get into your account but have lost your device.

What they did, though, when they set up their system is in the app and the registration process, they ONLY use the defaults (each code has a 30 second “period”, every code is 6 digits long, and the SHA1 hash is the core algorithm, with an 80-bit secret key; this key is the only piece of actual information they provide as it is the only thing that changes between users).  The fact that they only implement these defaults means that the Google Authenticator app is basically ONLY usable for Google sign-in, unless you happen to have a system that uses the EXACT same defaults.  I’ve tried using different URI strings on the app, and the app ignores them all, and it even truncates any secret keys you provide that are longer than 80 bits.  Frustrating.

(Note the program also supports HOTP, defined in RFC 4226, but time-based codes are more common in the "wild", and I'm not discussing the HOTP side of the app in this article)

WHAT NEEDS TO CHANGE

First, they can update their app to fully implement the standard URI format and allow for different “periods”, digit-lengths, hash algorithms (such as anything in the SHA family including the new SHA-3 standard when it finally gets codified by NIST, or other widely popular algorithms, like WHIRLPOOL, RipeMD160, etc.), and for any length key between 80- and 512-bit.  This would allow a larger number of users and system administrators to use the Google app as the standard for user-device acceptance.  The Bring Your Own Device (BYOD) movement is already strong in the marketplace, so why not take advantage of it.  RSA doesn’t have to be the only leader in this field.

Second, they could update their system to use stronger secret keys (>80 bits), and a better algorithm (like SHA2-384) as their own, new defaults.  Since they already have to have a DB or a table somewhere with these 80-bit keys for their users, they could add the fields necessary to store the additional system information (or simply widen the fields for the longer keys) and a flag for which algorithm the user is enrolled with, such as the v1 algorithm (SHA1) or v2 algorithm (SHA2-something).  This wouldn’t increase their data usage by any unmanageable amount and still allow their legacy two-factor users to log-in until they updated to the new order.

In the meantime, Google has released their Authenticator code to the world on their code repository site (http://code.google.com/p/google-authenticator/), so any other programmers could take up that torch and do what they are doing only better, and then Google could just publish their updated standard to allow the app writers to adjust.

Either way, as much as their two-factor system is a great start for security, in the wake of GPU-based password hash crackers and the bevy of NSA leaks, I think they need to be doing a lot more going forward.

LET ME EXPLAIN MY PARANOIA FROM A CRYPTO PERSPECTIVE

First off, the old DES encryption standard uses a 56-bit key, which with modern hardware, can be deciphered in about 4 minutes (using specialized, but not terribly expensive equipment).  Eighty bits is not much longer, and given an attacker might be able to skim 2 or 3 codes from a user by “shoulder-surfing” his phone at an airport or coffee-shop, he can then use these new GPU-based hash crackers (which are freely available on the Internet, just search “hashcat” as one example), he could brute-force attempt to find out what the 80-bit key is. 

The attacker can verify his efforts, even if he doesn’t know the target’s username/password, by using the time-codes that would have been used by the target’s phone/device when the attacker surfed the TOTP codes to reproduce them (remember, all cellphone companies broadcast the time to within about 30 seconds of most Internet time servers, so that information is not difficult to get at all, and we all know the algorithm Google uses to generate those time-codes, and that the TOTP-codes are produced every 30 seconds by default).

The TOTP algorithm does have a method to try to thwart such a reverse-engineering attempt (the final 32-bits used internally to generate the final 6-digit TOTP-code are taken from different parts of the internal hash based on a value in that hash, so the location changes every time), but at 80 bits, Google’s current secret just isn’t long enough to stave off modern (and especially distributed) brute-force efforts that ignore that extra step.

TL;DR

Google, increase your secret-key lengths, and make your app more adoptable by, I dunno, adopting the full standard in the first place!

Thursday, September 5, 2013

PBKDF2 in Almost Any HMAC Flavor You Want In C# .NET 4

using System;
using System.Text;
using System.Security.Cryptography;

namespace System.Security.Cryptography
{
    //Generic PBKDF2 Class that can use any HMAC algorithm derived from the
    // System.Security.Cryptography.HMAC abstract class
  
    // PER SPEC RFC2898 with help from user Dodgyrabbit on StackExchange
    // http://stackoverflow.com/questions/1046599/pbkdf2-implementation-in-c-sharp-with-rfc2898derivebytes
  
    // the use of default values for parameters in the functions puts this at .NET 4
    // if you remove those defaults and create the required constructors, you should be able to drop to .NET 2

    // USE AT YOUR OWN RISK!  I HAVE TESTED THIS AGAINST PUBLIC TEST VECTORS, BUT YOU SHOULD
    // HAVE YOUR CODE PEER-REVIEWED AND SHOULD FOLLOW BEST PRACTICES WHEN USING CRYPTO-ANYTHING!
    // NO WARRANTY IMPLIED OR EXPRESSED, YOU ARE ON YOUR OWN!
  
    // PUBLIC DOMAIN!  NO COPYRIGHT INTENDED OR RESERVED!    


    //constrain T to be any class that derives from HMAC, and that exposes a new() constructor
    public class PBKDF2<T>: DeriveBytes where T : HMAC, new()
    {
        //Internal variables and public properties
        private int _blockSize = -1;  // the byte width of the output of the HMAC algorithm     
        byte[] _P = null;
        int _C = 0;
        private T _hmac;

        byte[] _S = null;
        // if you called the initializer/constructor specifying a salt size,
        // you will need this property to GET the salt after it was created from the crypto rng!
        // GET THIS BEFORE CALLING GETBYTES()!  OBJECT WILL BE RESET AFTER GETBYTES() AND
        // SALT WILL BE LOST!!
        public byte[] Salt { get { return (byte[])_S.Clone(); } }

        // Constructors
        public PBKDF2(string Password, byte[] Salt, int IterationCount = 1000)
        { Initialize(Password, Salt, IterationCount); }

        public PBKDF2(byte[] Password, byte[] Salt, int IterationCount = 1000)
        { Initialize(Password, Salt, IterationCount); }

        public PBKDF2(string Password, int SizeOfSaltInBytes, int IterationCount = 1000)
        { Initialize(Password, SizeOfSaltInBytes, IterationCount);}

        public PBKDF2(byte[] Password, int SizeOfSaltInBytes, int IterationCount = 1000)
        { Initialize(Password, SizeOfSaltInBytes, IterationCount);}
      
        //All Construtors call the corresponding Initialize methods
        public void Initialize(string Password, byte[] Salt, int IterationCount = 1000)
        {
            if (string.IsNullOrWhiteSpace(Password))
                throw new ArgumentException("Password must contain meaningful characters and not be null.", "Password");
            if (IterationCount < 1)
                throw new ArgumentOutOfRangeException("IterationCount");
            Initialize(new UTF8Encoding(false).GetBytes(Password), Salt, IterationCount);
        }

        public void Initialize(byte[] Password, byte[] Salt, int IterationCount = 1000)
        {
            //all Constructors/Initializers eventually lead to this one which does all the "important" work
            if (Password == null || Password.Length == 0)
                throw new ArgumentException("Password cannot be null or empty.", "Password");
            if (Salt == null)
                Salt = new byte[0];
            if (IterationCount < 1)
                throw new ArgumentOutOfRangeException("IterationCount");
            _P = (byte[])Password.Clone();
            _S = (byte[])Salt.Clone();
            _C = IterationCount;
            //determine _blockSize
            _hmac = new T();
            _hmac.Key = new byte[] { 0 };
            byte[] test = _hmac.ComputeHash(new byte[] { 0 });
            _blockSize = test.Length;

        }

        public void Initialize(string Password, int SizeOfSaltInBytes, int IterationCount = 1000)
        {
            if (string.IsNullOrWhiteSpace(Password))
                throw new ArgumentException("Password must contain meaningful characters and not be null.", "Password");
            if (IterationCount < 1)
                throw new ArgumentOutOfRangeException("IterationCount");
            Initialize(new UTF8Encoding(false).GetBytes(Password), SizeOfSaltInBytes, IterationCount);
        }

        public void Initialize(byte[] Password, int SizeOfSaltInBytes, int IterationCount = 1000)
        {
            if (Password == null || Password.Length == 0)
                throw new ArgumentException("Password cannot be null or empty.", "Password");
            if (SizeOfSaltInBytes < 0)
                throw new ArgumentOutOfRangeException("SizeOfSaltInBytes");
            if (IterationCount < 1)
                throw new ArgumentOutOfRangeException("IterationCount");
            // You didn't specify a salt, so I'm going to create one for you of the specific byte length
            byte[] data = new byte[SizeOfSaltInBytes];
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            rng.GetBytes(data);
            // and then finish initializing...
            // Get the salt from the Salt parameter BEFORE calling GetBytes()!!!!!!!!!!!
            Initialize(Password, data, IterationCount);
        }

        ~PBKDF2()
        {
            //*DOOT* clean up in aisle 5! *KEKERKCRACKLE*
            this.Reset();
        }

        // required by the Derive Bytes class/interface
        // this is where you request your output bytes after Initialize
        // state of class Reset after use!
        public override byte[] GetBytes(int ByteCount)
        {
            if (_S == null || _P == null)
                throw new InvalidOperationException("Object not Initialized!");
            if (ByteCount < 1)// || ByteCount > uint.MaxValue * blockSize)
                throw new ArgumentOutOfRangeException("ByteCount");

            int totalBlocks = (int)Math.Ceiling((decimal)ByteCount / _blockSize);
            int partialBlock = (int)(ByteCount % _blockSize);
            byte[] result = new byte[ByteCount];
            byte[] buffer = null;
            // I'm using TT here instead of T from the spec because I don't want to confuse it with
            // the generic object T
            for (int TT = 1; TT <= totalBlocks; TT++)
            {
                // run the F function with the _C number of iterations for block number TT
                buffer = _F((uint)TT);
                //IF we're not at the last block requested
                //OR the last block requested is whole (not partial)
                //  then take everything from the result of F for this block number TT
                //ELSE only take the needed bytes from F
                if (TT != totalBlocks || (TT == totalBlocks && partialBlock == 0))
                    Buffer.BlockCopy(buffer, 0, result, _blockSize * (TT - 1), _blockSize);
                else
                    Buffer.BlockCopy(buffer, 0, result, _blockSize * (TT - 1), partialBlock);
            }
            this.Reset();  // force cleanup after every use!  Cannot be reused!
            return result;
        }

        // required by the Derive Bytes class/interface
        public override void Reset()
        {
            _C = 0;
            _P.Initialize(); // the compiler might optimize this line out! :(
            _P = null;
            _S.Initialize(); // the compiler might optimize this line out! :(
            _S = null;
            if (_hmac != null)
                _hmac.Clear();
            _blockSize = -1;
        }

        // the core function of the PBKDF which does all the iterations
        // per the spec section 5.2 step 3
        private byte[] _F(uint I)
        {
            //NOTE: SPEC IS MISLEADING!!!
            //THE HMAC FUNCTIONS ARE KEYED BY THE PASSWORD! NEVER THE SALT!
            byte[] bufferU = null;
            byte[] bufferOut = null;
            byte[] _int = PBKDF2<T>.IntToBytes(I);
            _hmac = new T();
            _hmac.Key = (_P); // KEY BY THE PASSWORD!
            _hmac.TransformBlock(_S, 0, _S.Length, _S, 0);
            _hmac.TransformFinalBlock(_int, 0, _int.Length);
            bufferU = _hmac.Hash;
            bufferOut = (byte[])bufferU.Clone();
            for (int c = 1; c < _C; c++)
            {
                _hmac.Initialize();
                _hmac.Key = _P;  // KEY BY THE PASSWORD!
                bufferU = _hmac.ComputeHash(bufferU);
                _Xor(ref bufferOut, bufferU);
            }
            return bufferOut;
        }

        // XOR one array of bytes into another (which is passed by reference)
        // this is the equiv of data ^= newData;
        private void _Xor(ref byte[] data, byte[] newData)
        {
            for (int i = data.GetLowerBound(0); i <= data.GetUpperBound(0); i++)
                data[i] ^= newData[i];
        }

        // convert an unsigned int into an array of bytes BIG ENDIEN
        // per the spec section 5.2 step 3
        static internal byte[] IntToBytes(uint i)
        {
            byte[] bytes = BitConverter.GetBytes(i);
            if (!BitConverter.IsLittleEndian)
            {
                return bytes;
            }
            else
            {
                Array.Reverse(bytes);
                return bytes;
            }
        }
    }
}

Tuesday, June 25, 2013

Goodbye, Sprint

So, after 15+ years of being with the same carrier, I'm finally tired of putting up with their shit.  Mostly their prices, the service hasn't been... notable (I'll put it that way).

But this really made up my mind:


Reference Number: [REDACTED]
DATE/TIME: 2013-06-25 11:xx:xx

Your Sprint chat transcript


Megan: Thank you for visiting Sprint. What questions can I answer for you today?

You: Hi Megan, my question is, does Sprint offer anything less that the Everything ___ plans for smart phones? I ask because I have a plan now that has a ton of minutes in it that I never use.

You: I'd rather pay for the data, and a much smaller amount of minutes and texting.

Megan: I am sorry, the Everything Data is the most economical plan we've for smartphones.

You: See, it's the word economical that gets me. Ting and Virgin have plans that more match my actual usage that would range between $55-70/mo versus the $167 I'm paying now.

You: Is there anything that Sprint can do for me to match what they offer?

Megan: I can understand your concern. However the Everything Data is the most economical plan we've with Sprint.

You: I see... For my record, I have two lines with different contract lengths left, what would be my liability for early termination?

You: Are you able to provide an "exact" dollar amount?

Megan: I’m happy to pull up your account and take a look at that for you.

Megan: What is the account's primary telephone number?

[ACCOUNT DETAILS REDACTED]

Megan: Thank you, one moment please while I pull up your account.

You: ok

[LONG PAUSE]

Megan: Thank you for holding.

Megan: I appreciate your patience!

You: no problem

Megan: If you cancel your 
[REDACTED, WIFE] line you need to pay $180 as early termination fee.

Megan: If you cancel your 
[REDACTED, MINE] line you need to pay $100 as early termination fee.

You: ok, that line has longer to go, so that makes sense

You: ok, so as of today, that's $280. If I go with a new provider, at only say $60/mo, I'd have that fee paid off in approximately 3 months.

You: I'm assuming those fees go down the closer I get to the end of the contract? If so, by how much?

[YOUR TL;DR; MOMENT RIGHT HERE, SPRINT'S TERMINATION FEE POLICY SPELLED OUT, underlining added by me for emphasis]

Megan: From the 15th day of service to the end of the sixth month, the Early Termination Fee will be $350. When you have 17 months remaining, the Early Termination Fee will decrease by $20 with each additional month of service. With 5 months remaining until the end of the term, the ETF is only $100 if you decide to discontinue service.

You: Ok. Thank you. You've helped me formulate a decision that I need to confer with my wife.

Megan: You're welcome.

Megan: Are there any additional questions I can help with today?

You: Not that I can think of at the moment. Please attach a copy of this conversation to my account. And have a wonderful week. Thank you for your time. :)

Megan: You can have a copy of this chat conversation sent to your Email address by clicking the envelope icon in the upper right hand corner.

You: I will do that, thank you.

Megan: Thank you for visiting www.sprint.com.

Megan: My name is Megan and it has been a pleasure assisting you. Have a great day.

You: Take care, Megan :)

Megan: Bye and take care.



She was helpful, courteous, patient, and I have no complaints on this conversation, aside from their ridiculous prices, which is not HER fault.  But my mind is made up... Ting uses the same network (they buy it from Sprint, which is kind of ironic on some levels), so the service should be the same, but $100 cheaper /mo.

Time to say "Ting, how you 'doin?"  I just hope they let me keep my Detroit number...

[UPDATE:  Easiest carrier change I've ever done!  And I got to keep BOTH numbers I had with Sprint even though one is from Detroit.  The number portability act makes that possible.  I had to have Ting start the process to pull the numbers from Sprint so I could keep them, which meant giving them my social security # and the account pin at Sprint.  This always makes me cautious by nature, but I know why they did it.  They had to have proof that I was indeed switching to a new carrier, and not some scammer trying to steal my service. 

TIP:  DON'T CANCEL YOUR PLANS/ACCOUNTS BEFORE YOU START WITH A NEW CARRIER, otherwise you'll lose your number.  If your account is closed or grossly overdue, they have no obligation to let you keep your number!

Meanwhile Sprint responded to my tweet about switching:
I told them their fees were "crap" and that they don't offer any reasonable plans.  Of course I learned about Sprint being acquired by a Japanese telecom [warning: video] only AFTER I switched (this isn't new news, but it was news to me, I was clueless), and that they'd be offering new plans soon, but the new plans are still too expensive, and I'm done with constantly having a contract hovering over my head. Too Little, Too Late]

(use promo code at http://chat.ting.com to help out the Kevin Pollak Chat Show if you want, it's worth $25 bucks to you, and Kevin has a great podcast if you haven't listened to/watched it yet, DO IT!  When that one stops working, use mine, you get $25, I get $25: Referral)

Sunday, June 2, 2013

CAML Queries for finding Document Sets

So you have Document Sets in your library, and you need to list only them, or you are trying to find one in particular.   But when your CAML queries are looking for specific document sets and field values, they turn up nothing.

Well there are two ways around this.  The first involves using a query for a folder object type.

<Eq>
  <FieldRef Name='FSObjType' />
  <Value Type='Number'>1</Value>
</Eq>

This looks for folders specifically, but it also seems to help with Document Sets.  This one is also well documented.

There is another way I found when I was using a tool that wouldn't let me pick FSObjType as a field because it really isn't a field so much as a Content DB... thing.  There is a hidden field called "Level" (internal name "_Level" which you must use in CAML) which appears to indicate the folder level within the library where an item resides.

So if you have a content type derived from Document Set called... ummm... "My Document Sets" for example, and they reside in a library at the root folder, you can find the one you want doing something like this:

<Where>
<And>
  <Something>
     this is where you would search for some other criteria, like a name, title, description, whatever
  </Something>
  <And>
    <Eq>
      <FieldRef Name='_Level' />
      <Value Type='Number'>1</Value>
    </Eq>
    <Eq>
      <FieldRef Name='ContentType' />
      <Value Type='Text'>My Document Sets</Value>
    </Eq>
  </And>
</And>
</Where>

With the Level field specified it seems to force SharePoint to include folders at the level specified.  This will get you around using some other tool or product that won't let you specify FSObjType, but if that tool or product can find hidden fields, Level should be in there.

Good luck.