A case for using only three different digits in keypad codes 3

Posted by Jonas Elfström Sun, 27 Sep 2009 19:02:00 GMT

Keypads have obvious security problems and keypads accepting a stream of digits with no # or enter in between, while checking for the four digit long code, are even worse.

The important part is to not leak the digits in the code by wear or intentional markings because if they leak it's suddenly very far from 10000 combinations.

If the "lock picker" only knows that the code contains four digits there are 10000 combinations. Keypads accepting a stream of digits can then be opened in a maximum of 10003 keystrokes using the De Bruijn sequence. That is still quite a lot.

Below is a Ruby implementation of the De Bruijn sequence.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# De Bruijn sequence
# Original implementation by Frank Ruskey (1994)
# translated to C by Joe Sawada
# and further translated to Ruby by Jonas Elfström (2009)

@n=4
@k=10
@a=[0]
@sequence=[]

def debruijn(t, p, alike)
  if t>@n
    if @n%p==0
      1.upto(p) {|j| @sequence<<@a[j]}
    end
  else
    @a[t]=@a[t-p]
    if @a[t]>0
      debruijn(t+1,p,alike+1)
    else
      debruijn(t+1,p,alike)
    end
    (@a[t-p]+1).upto(@k-1) {|j|
      @a[t]=j
      debruijn(t+1,t,alike+1)
    }
  end
end

debruijn(1,1,0)
print @sequence.join
 

It's not uncommon to find keypads with 4 of the 10 keys worn down and if you do you can be pretty sure that the code contains those four different digits. The number of possible combinations are 4! = 4x3x2x1 = 24. I got curious to see if there's a kind of De Bruijn sequence for this that brings down the 4*24=96 keystrokes. By scribbling in a text editor I quickly realized there's not a clean sequence. Not clean in the way that a sequence following the rules can be created. Also it's probably even quite daunting to present it as mathematically dense and beautiful as the De Bruijn but that could be my less than great combinatorics speaking.

I made a quick and dirty brute force hack to try to find a shorter sequence.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
seq=[]
1.upto(4) {|a| 1.upto(4) {|b| 1.upto(4) {|c| 1.upto(4) {|d|
  seq << "%d%d%d%d" % [a,b,c,d] if !(a==b || a==c || a==d || b==c || b==d || c==d)
}}}}

s=seq[0]
seq.delete_at(0)
while (seq.length>0)
  next_code=(seq.select {|c| c[0..2]==s[-3..-1]})
  if next_code.empty?
    next_code=(seq.select {|c| c[0..1]==s[-2..-1]})
    if next_code.empty? 
      next_code=(seq.select {|c| c[0]==s[-1]})
      s+=next_code[0][1..3]
      seq.delete_at(seq.index(next_code[0]))
    else
      s+=next_code[0][2..3]
      seq.delete_at(seq.index(next_code[0]))
    end
  else
    next_code=(seq.select {|c| c[0..2]==s[-3..-1]})
    s+=next_code[0][3].chr
    seq.delete_at(seq.index(next_code[0]))
  end
end

The above code takes the first code "1234" of the 24 and then searches the rest of the array for a code beginning with "234". It finds "2341" and adds "1" to the end of s and continues to look for "341" and so on. Relatively soon there is no three digit match and then it tries two digits and eventually even that fails and then it gets the first one digit match. The resulting sequence is:

123412314231243121342132413214321

From 96 to 33 keystrokes. Not as effective as De Bruijn but still significant. Unlike De Bruijn I have absolutely no proof that this is the shortest one possible but it seems likely. Also notice that in the middle of the sequence we find "3121" and "1213". Those break the criteria of four different digits but they seem to be necessary to be able to enter the reversed mode. Try reading the sequence forward and backwards to see what I mean.

If the code only contains two digits it's gets even more trivial to try them all. There are 14 possible codes and by compressing those to one sequence you get down to 20 keystrokes.

Things get a little more interresting if three buttons are worn. It turns out that the repeated digits can be placed in the code in six different ways.

0012,1002,1200,0102,0120,1020

That's 6x2x3=36 combinations and, maybe a little unintuitive, 12 more than if you are using four different digits. I compressed it down to 49 key strokes. Unlike the sequence for four different digits I can't find it with google and I know it's kind of security by obscurity but that could give a tiny amount of extra trouble to an attacker. I will not be the first one publishing it.

Be aware that if an attacker knows you are using a 0012-like code he gets a smaller space to search. 6x8x9x10=4320 instead of 10000. You have to weight the risk of button leaks against a code protocol leak.

Why you should use four different digits for keypad locks 3

Posted by Jonas Elfström Wed, 23 Sep 2009 17:49:00 GMT

I made a couple of very bad mistakes in this article so I took it down. Hopefully I'm more on track in the sequel.

Breaking simple ciphers 2

Posted by Jonas Elfström Wed, 16 Sep 2009 18:19:00 GMT

The last few days I've happened to stumble over a couple of ciphers and I just couldn't help myself from trying to break them.



The Lost Symbol

Dan Brown has a new book coming out and part of the promotion is this cipher text "AOFACFSOA FSZWBEIC EIOA ZOHSFWQWOA OQQSDW". The WQW, QQ and three of the words ending with an A made me believe we could be dealing with a substitution cipher and maybe even a Caesar cipher, the most simple of them all.

As usual my tool of choice was Ruby and in this case the splendid Interactive Ruby Shell.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
$ irb
>> s="AOFACFSOA FSZWBEIC EIOA ZOHSFWQWOA OQQSDW"
=> "AOFACFSOA FSZWBEIC EIOA ZOHSFWQWOA OQQSDW"
>> def caesar(text,n)
>>   alphas=('A'..'Z').to_a*2
>>   text.tr('A-Z', alphas[n..n+26].join)
>> end
>> 1.upto(25) do |n| puts "%2d. %s" % [n, caesar(s,n)] end
 1. BPGBDGTPB GTAXCFJD FJPB APITGXRXPB PRRTEX
 2. CQHCEHUQC HUBYDGKE GKQC BQJUHYSYQC QSSUFY
 3. DRIDFIVRD IVCZEHLF HLRD CRKVIZTZRD RTTVGZ
 4. ESJEGJWSE JWDAFIMG IMSE DSLWJAUASE SUUWHA
 5. FTKFHKXTF KXEBGJNH JNTF ETMXKBVBTF TVVXIB
 6. GULGILYUG LYFCHKOI KOUG FUNYLCWCUG UWWYJC
 7. HVMHJMZVH MZGDILPJ LPVH GVOZMDXDVH VXXZKD
 8. IWNIKNAWI NAHEJMQK MQWI HWPANEYEWI WYYALE
 9. JXOJLOBXJ OBIFKNRL NRXJ IXQBOFZFXJ XZZBMF
10. KYPKMPCYK PCJGLOSM OSYK JYRCPGAGYK YAACNG
11. LZQLNQDZL QDKHMPTN PTZL KZSDQHBHZL ZBBDOH
12. MARMOREAM RELINQUO QUAM LATERICIAM ACCEPI
13. NBSNPSFBN SFMJORVP RVBN MBUFSJDJBN BDDFQJ
14. OCTOQTGCO TGNKPSWQ SWCO NCVGTKEKCO CEEGRK
15. PDUPRUHDP UHOLQTXR TXDP ODWHULFLDP DFFHSL
16. QEVQSVIEQ VIPMRUYS UYEQ PEXIVMGMEQ EGGITM
17. RFWRTWJFR WJQNSVZT VZFR QFYJWNHNFR FHHJUN
18. SGXSUXKGS XKROTWAU WAGS RGZKXOIOGS GIIKVO
19. THYTVYLHT YLSPUXBV XBHT SHALYPJPHT HJJLWP
20. UIZUWZMIU ZMTQVYCW YCIU TIBMZQKQIU IKKMXQ
21. VJAVXANJV ANURWZDX ZDJV UJCNARLRJV JLLNYR
22. WKBWYBOKW BOVSXAEY AEKW VKDOBSMSKW KMMOZS
23. XLCXZCPLX CPWTYBFZ BFLX WLEPCTNTLX LNNPAT
24. YMDYADQMY DQXUZCGA CGMY XMFQDUOUMY MOOQBU
25. ZNEZBERNZ ERYVADHB DHNZ YNGREVPVNZ NPPRCV

Take a closer look at row 12.

MARMOREAM RELINQUO QUAM LATERICIAM ACCEPI

I found Rome a city of bricks and left it a city of marble. - Google tells me it's Augustus.

The code is not the most clear I've written but if you read Ruby in your sleep you can skip this part.

('A'..'Z') is a range in Ruby. Another, maybe more obvious, example of a range is (0..7).

.to_a could be read as to_array and unsurprisingly it converts a range to an array. (0..7).to_a will create [0, 1, 2, 3, 4, 5, 6, 7]

The operator * for arrays appends n copies of the array. Thus [0,1,2]*2 will create [0,1,2,0,1,2].

String#tr works the same way as the Unix command tr, it translates the characters in the string according to the from and to parameters.

At last .join converts the array to a string.

 

The recruiting agency

A government agency responsible for signals intelligence is hiring. Among the qualifications they are looking for is the ability to break a certain cipher. I will not publish their cipher here but instead one of my own, constructed in the same way as theirs.

"VGhpcyBpcyBleGNsdXNpdmUgZm9yIHlvdSwgb3I/IGMNR0d
LCkZPXgpTRV8K\nTENEQ1lCCkhfXgpoT1NFRElPCkNZCksKSE9eXk
9YCklYU1peRU1YS1pCT1gK\nXkJLRAp5SUJET0NPWAQ="
At first glance it looked like Base64 and the ending "=" made it even more likely.
1
2
3
4
5
$ irb
>> require 'base64'
>> cipher = "VGhpcyBpcyBleGNsdXNpdmUgZm9yIHlvdSwgb3I/IGMNR0dLCkZPXgpTRV8K\nTENEQ1lCCkhfXgpoT1NFRElPCkNZCksKSE9eXk9YCklYU1peRU1YS1pCT1gK\nXkJLRAp5SUJET0NPWAQ="
>> decoded=Base64.decode64(cipher)
=> "This is exclusive for you, or? c\rGGK\nFO^\nSE\nLCDCYB\nH^\nhOSEDIO\nCY\nK\nHO^^OX\nIXSZ^EMXKZBOX\n^BKD\nyIBDOCOX\004"

So it's Base64 but to no surprise it didn't end there. The "This is exclusive for you, or?" hinted at XOR so I tried XORing the text with 0-255.

1
2
3
4
5
6
7
8
>> code=decoded[31..decoded.length].split(//)
>> File.open('xor.txt','w') { |file|
?>   0.upto(255) {|n|
?>     file.write(n.to_s + " ")
>>     code.each {|c| file.write( (c[0]^n).chr ) }
>>     file.write("\n\n")
>>   }
>> }

A quick look in the file told me that XORing with 42 was the solution.

Now you know how to break two of the most simple cipher methods. Use the knowledge wisely. :)

Setting charset with a before filter in Sinatra 2

Posted by Jonas Elfström Fri, 31 Jul 2009 06:30:00 GMT

In the git example in my previous post about Sinatra and Heroku I happened to mention a filter for using UTF-8. I remember finding it somewhere on the net but I can't remember where and it seems my current google-fu is as weak as my memory. Anyway, it looks like this:

1
2
3
4
5
6
7
8
9
10
11
CONTENT_TYPES = {:html => 'text/html', :css => 'text/css', 
                :js  => 'application/javascript'}

before do
 request_uri = case request.env['REQUEST_URI']
   when /\.css$/ : :css
   when /\.js$/  : :js
   else          :html
 end
 content_type CONTENT_TYPES[request_uri], :charset => 'utf-8'
end

An average application in the clouds

Posted by Jonas Elfström Thu, 11 Jun 2009 21:03:00 GMT

A couple of days ago I published my first applicaiton "in the cloud". I named it Average and it's only a tiny little app that I did for fun and learning. It's written in Ruby with the micro framework Sinatra, DataMapper, Haml and published to Heroku with their git-based workflow. Except Ruby I hadn't used any of these before.

The tools
Sinatra will most likely become my favorite tool for quick web hacks in the futute. I've been using Rubys' built-in CGI support for such before, but Sinatra is beyond compare. It may not be very well suited for anything but small projects but it's hand in glove for admin pages, "reports" or REST services. Still there are some not that small projects on http://www.sinatrarb.com/wild.html

DataMapper is an ORM and at glance it looks a lot like ActiveRecord but it feels more "pure". I really like that it uses regular Ruby classes (and methods, no method_missing magic) that you include DataMapper to. Sadly the documentation is terrible, it's unstructured and missing some crucial bits. Hopefully that will change in the near future and since it's a wiki anyone can help.

Haml is a markup language that generates HTML/XHTML. It makes it very easy to create fully validating XHTML pages. Some parts of Hamls felt kind of strange but after using it for a couple of hours it all seemed to fall into place and I started to like it more and more by the minute. As a disclaimer I have to admit to only have created 8-9 simple Haml templates ever.

git is a distributed revision control tool. It's really fast, faster than any SCM I've ever used. Git was initially developed by Linus Torvalds and http://github.com/ seems to be a driving force of adoption.

Heroku is very impressive! Unparalleled ease of deployment and if you need more power just slide the "dyno slider"! Heroku is free as long as you only use one dyno, but if you need more "CPUs", backups, more than 5MB and so on, there's a fee.

Under the hood
Average doesn't do much but to its defense it does it in few lines of code. One single Ruby file and some views:
$ wc -l average.rb
  196 average.rb
$ wc -l views/*.haml
  6 views/average.haml
 19 views/index.haml
 25 views/new.haml
  9 views/notenoughdata.haml
 13 views/poll.haml
 16 views/show.haml
284 rows total.
And there's nothing more to it because the data model is included in the average.rb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Average
  include DataMapper::Resource
  property :id, Integer, :serial => true
  property :name, String, :length => 50
  property :unit, String, :length => 50
  property :what, String, :length => 50
  property :public, Boolean, :default => true
  property :avgthreshold, Integer, :default => 0
  property :cryptokey, String
  property :adminkey, String
  property :allow_multiple, Boolean, :default => false
  has n, :values
end

class Value
  include DataMapper::Resource
  property :id, Serial
  property :value, Float
  property :valuekey, String
  belongs_to :average
end


I only had to type one single SQL statement:
CREATE DATABASE average;
The tables were created by executing DataMapper.auto_migrate! (in irb), it's also possible to migrate table by table: Average.auto_migrate!. Migrate drops the table so if you want to keep the data you can use Averages.auto_upgrade!. It's real easy and powerful.
Heroku also has a console and there you can run Ruby code in the running production enviroment.
$ heroku console
>> DataMapper.auto_migrate!

To push your local database to the server just:
$ heroku db:push mysql://user:pwd@localhost/average
(Heroku runs PostgreSQL but the above translates from a plethora of databases. I got some kind of problem with Booleans from MySQL though.)
For ActiveRecord there's rake db:...

A typical Sinatra Route (a HTTP method paired with a URL matching pattern) could look something like this:

1
2
3
4
get '/' do
  @avgs=Average.all(:public => true, :limit => 25, :order => [:id.desc])
  haml :index
end


My views are written in Haml and it looks something like this:

1
2
3
4
%h1= @avg.name
%p== The average #{@avg.what} is #{average} #{@avg.unit} and the sample size is #{sample_size}.
- if @done==false then
  %a{:href=>"/poll/"+@avg.cryptokey} Add value


Heroku uses git to publish the applications and the workflow is:
git add.
git commit -m "added a before filter for charset utf-8"
git push heroku

PS. Peter, look, it validates! http://validator.w3.org/check?uri=http%3A%2F%2Faverage.heroku.com :) DS

Older posts: 1 2 3 4