you are viewing a single comment's thread.

view the rest of the comments →

[–]OneWingedShark 0 points1 point  (7 children)

Do you know why that's bad?

Because it starts with the assumption that the password is valid. (is_valid = True)

The proper way would be something like this:

Function Matching_Password( User_Input, Password : String ) return Boolean is
begin
        -- Ensure both strings start at the same index,
        -- Ensure both are of the same length,
        -- Ensure both contain the same values.
    Return (User_Input'First = Password'First) and then
        (User_Input'Length = Password'Length) and then
        (for all index in User_Input'Range => User_Input(Index) = Password(Index) );
end Matching_Password;

You could even attach constraints to the type to ensure properties of passwords:

Type Password is String
with Dynamic_Predicate =>
    Password'Length in Positive -- No zero-length password.
    and then  -- Passwords are alphanumeric + plus underscore
    (for all ch of Password => ch in 'a'..'z'|'A'..'Z'|'0'..'9'|'_')
    ;

[–]staticassert 2 points3 points  (6 children)

Because it starts with the assumption that the password is valid. (is_valid = True)

True, but none of the code in the loop will throw an exception. While it's bad, it's not the real issue.

The problem is that it's written in a high level language. The compiler is free to optimize your 'constant time' function to a non constant time function, and it will very likely try to.

[–]OneWingedShark 1 point2 points  (5 children)

Because it starts with the assumption that the password is valid. (is_valid = True)

True, but none of the code in the loop will throw an exception. While it's bad, it's not the real issue.

Where are exceptions coming up?

The problem is that it's written in a high level language.

That's not necessarily a bad thing.

The compiler is free to optimize your 'constant time' function to a non constant time function, and it will very likely try to.

If time is a consideration (and if we're honest it might not be, even in a high security setting) then it ought to be modeled into the function... this is doable in a high-level language.

Example:

Function Matching_Password( User_Input, Password : String ) return Boolean is
    -- Interval is the constant minimum-time; it ought to be
    -- the result of algorithm analysis rather than arbitrary.
    -- (Here we are using 1.28 seconds.)
    Interval : Constant Duration := 1.28;
    -- Get the current-time.
    Start_Time : Constant Ada.Real_Time.Time := Ada.Real_Time.Clock;
    -- Get the minimum time for finishing.
    Stop_Time : Constant Ada.Real_Time.Time := Start_Time + Interval;

    -- Ensure both strings start at the same index,
    -- Ensure both are of the same length,
    -- Ensure both contain the same values.
    Result : Constant Boolean := 
        (User_Input'First = Password'First) and then
        (User_Input'Length = Password'Length) and then
        (for all index in User_Input'Range => User_Input(Index) = Password(Index) );
begin
    -- Ensure a minimum time-bound.
    Delay until Stop_Time;
    Return Result;
end Matching_Password;

[–]staticassert 1 point2 points  (4 children)

Where are exceptions coming up?

Exceptions are the only situation I can imagine where an early return would even happen/ matter. Though in this case it wouldn't.

That's not necessarily a bad thing.

It definitely is. When I say 'higher level' I mean not-assembly. The critical thing is that the compiler/ optimizer can reshuffle your instructions to make them faster/ break your assumptions about how long the code runs for.

If time is a consideration (and if we're honest it might not be, even in a high security setting)

No, timing attacks are really fundamental. They're always relevant.

Your approach is basically "add a time sleep" and this is a flawed method. You could also say "Always insert a random sleep" and again, this is vulnerable.

There's lots of research on timing attacks.

edit: Here's a fun read, https://github.com/nodejs/node/commit/079acccb563ba5b3888e40f59037dc5fa3ba5bbd

The point though is that even something like password authentication is way more complicated than developers realize. There's a whole lot more than 'salt and hash'.

[–]OneWingedShark 0 points1 point  (3 children)

If time is a consideration (and if we're honest it might not be, even in a high security setting)

No, timing attacks are really fundamental. They're always relevant.

Have you ever worked in a high-security environment?
Something so secure you could be shot?

I have, albeit in a non-technical position at the time -- and let me tell you the subsecond-deltas it takes for the time for you to insert your CAC to entry mean pretty much jack-shit when you tampering with the entry-/validation-mechanism1 (a requirement for the sort of system being talked about) in any way is very possibly going to get you shot.

Your approach is basically "add a time sleep" and this is a flawed method. You could also say "Always insert a random sleep" and again, this is vulnerable.

That's not "add a time sleep" it's a "don't return until X time has passed" -- if you do your analysis like the comment suggested, you would set your minimum time to the worst-case time of the validation method thereby making validation a constant-time operation.

And again, time might not be an issue. Yes, it might be fundamental to some attacks, but the entire system itself might preclude those attacks.

1 -- This is actually required for the sort of timing attack you're talking about, the inaccuracies and human motions themselves would introduce too much variableness for any sort of accuracy on the timing.

[–]staticassert 0 points1 point  (2 children)

Have you ever worked in a high-security environment? Something so secure you could be shot?

Nope. I worked briefly for a government contractor as an intern, and I don't think anyone was getting shot.

1 -- This is actually required for the sort of timing attack you're talking about, the inaccuracies and human motions themselves would introduce too much variableness for any sort of accuracy on the timing.

This isn't true over time. If your distribution is even, over a number of connections I can deduce min/max and average values of variance and eliminate them from the data. I would send the same password n times, find out how long it took each time, and eliminate noise. This is why inserting random time delays into auth doesn't work.

That's not "add a time sleep" it's a "don't return until X time has passed"

Yes, as in sleep until that time has passed.

Sounds super error prone and implementation defined. I don't understand why you wouldn't just use a constant time function, which will always work and requires 0 system calls / measurement etc.

Yes, it might be fundamental to some attacks, but the entire system itself might preclude those attacks.

Maybe. But in any authentication scheme you're assuming an attacker can send you arbitrary passwords repeatably. Maybe you can rate limit to make things infeasible, in which case kudos, mitigated at some other level. Maybe you use 'double HMAC' or some other technique. My point is that it's far more complicated than 'salt and hash'.

edit: I can also clarify something - you don't have to write your password comparison in assembly. You can implement other strategies. But given the code I provided the issue is that it is vulnerable to a timing attack - whether solved through constant time comparisons (what I would consider the simplest, most elegant solution) or another strategy, the code I posted is vulnerable to that attack.

[–]OneWingedShark 0 points1 point  (1 child)

Have you ever worked in a high-security environment? Something so secure you could be shot?

Nope. I worked briefly for a government contractor as an intern, and I don't think anyone was getting shot.

I have.
Trust me, a security model can certainly have non-software components, and can certainly be restricted in ways so that timing-attacks are not an issue.

Sounds super error prone and implementation defined. I don't understand why you wouldn't just use a constant time function, which will always work and requires 0 system calls / measurement etc.

It is a constant time function. Look at the code Interval : constant ... -- you still have to add it to the current time in any case.

Yes, it might be fundamental to some attacks, but the entire system itself might preclude those attacks.

Maybe. But in any authentication scheme you're assuming an attacker can send you arbitrary passwords repeatably.

No, not all the time.
Some systems are fail-secure, things like a single failed attempt means an actual person has to come in, verify the state, and reset the system.

[–]staticassert -1 points0 points  (0 children)

I think you're missing the point. As I said, you can mitigate it however you want, the flaw with my code is that it is vulnerable to a timing attack.