Skip to content

Instantly share code, notes, and snippets.

@mad
Created April 4, 2011 16:36
Show Gist options
  • Save mad/901932 to your computer and use it in GitHub Desktop.
Save mad/901932 to your computer and use it in GitHub Desktop.
#!/usr/bin/ruby
# -*- coding: utf-8 -*-
require 'rexml/document'
# Calculate a modular exponentiation eg: b^p mod m
# http://en.wikipedia.org/wiki/Modular_exponentiation
class Integer
def mod_pow(power, mod)
base, result = self.to_i, 1
while power > 0
result = (result * base) % mod if power & 1 == 1
base = (base * base) % mod
power >>= 1;
end
result
end
end
# Trick from here http://snippets.dzone.com/posts/show/302
class << Hash
def create(keys, values)
self[*keys.zip(values).flatten]
end
end
# Used for read xml file with all keys and get specified key
class CA
attr_accessor :xml_data, :caKeys
# XXX: catch exception
def initialize(filename)
xml = File.read(filename)
@xml_data, @caKeys = REXML::Document.new(xml), []
parse()
end
def parse
@xml_data.elements.each('caKeys/caKey') do |s|
h = {:rsaPublicKey => {}}
%w[rid caKeyIndex].each do |a|
h[a.intern] = s.elements[a].text
end
%w[exponent modulus].each do |a|
h[:rsaPublicKey][a.intern] = s.elements['rsaPublicKey'].elements[a].text
end
@caKeys << h
end
end
def getKey(rid, caKeyIndex)
# XXX: make it more simple? O(1)?
@caKeys.each do |key|
# Return first satisfied key
if key[:rid] == rid and key[:caKeyIndex] == caKeyIndex
return key
end
end
end
private :parse
end
# Used for inteprete tags from card and decrypt cert and static auth
class CardReader
attr_accessor :static_tags, :ca_key, :card_static_info, :static_auth
def initialize(card, ca_keys)
@static_tags = card
# Load all keys
ca = CA.new(ca_keys)
# Find us key
rid = @static_tags['4f - Application identifier (AID)'].unpack('a10')[0]
caKeyIndex = @static_tags['8f - Certification Authority Public Key Index']
@ca_key = ca.getKey(rid, caKeyIndex)
# Do main work
decrypt_static_cert()
decrypt_static_auth()
end
def decrypt_static_cert()
# card_static_info = cert^e mod n
card_static_info_raw = @static_tags['90 - Issuer Public Key Certificate'].hex.
mod_pow(@ca_key[:rsaPublicKey][:exponent].hex,
@ca_key[:rsaPublicKey][:modulus].hex)
mod_len_with_padding = (@static_tags['90 - Issuer Public Key Certificate'].size/2 - 36)
@card_static_info =
Hash.create(['prefix', # 1 byte
'format', # 1 byte
'partial_number', # 4 byte
'expire_date', # 2 byte
'serial', # 3 byte
'alg_hash', # 1 byte
'alg_sign', # 1 byte
'mod_len', # 1 byte
'exp_len', # 1 byte
'mod_em', # L(CA) - 36
'cert_hash', # 20 byte
'suffix' # 1 byte
], card_static_info_raw.to_s(16).unpack("a2a2a8a4a6a2a2a2a2a" + (mod_len_with_padding * 2).to_s + "a40a2"))
check_bounds(@card_static_info)
# Remove padding if exists
# XXX: hm, we may notify about that?
padding_len = mod_len_with_padding - @card_static_info['mod_len'].hex
if padding_len > 0
@card_static_info['mod_em'] = @card_static_info['mod_em'].chomp("bb" * padding_len)
end
end
def decrypt_static_auth()
mod = @card_static_info['mod_em']
if @static_tags['92 - Issuer Public Key Remainder'] != nil
mod = mod + @static_tags['92 - Issuer Public Key Remainder']
end
static_auth_raw = @static_tags['93 - Signed Static Application Data'].hex.
mod_pow(@static_tags['9f32 - Issuer Public Key Exponent'].hex, mod.hex)
padding_len = (@static_tags['93 - Signed Static Application Data'].size/2 - 26)
@static_auth =
Hash.create(['prefix', # 1 byte
'format', # 1 byte
'alg_hash', # 1 byte
'auth_code', # 2 byte
'padding', # L(ISSUER) - 26
'card_hash', # 20 byte
'suffix' # 1 byte
], static_auth_raw.to_s(16).unpack("a2a2a2a4a" + (padding_len * 2).to_s + "a40a2"))
check_bounds(@static_auth)
end
def check_bounds(data)
unless data['prefix'] == "6a" and data['suffix'] == "bc"
raise "Bad card info, prefix = #{data['prefix']} and suffix = #{data['suffix']}"
end
end
private :check_bounds
end
# It is my task
$card_static_var2 = {
'4f - Application identifier (AID)' => 'A0000000041010',
'8f - Certification Authority Public Key Index' => '22',
'90 - Issuer Public Key Certificate' => '0B7D07C36FF78C53CCC3448D4421A4C8' +
'AFA823883FA84C1B024D94B79E94C18C' +
'29E31E1AEB990B4882C22F37C1F1FDDB' +
'65066CB4A6E2245234CD7847B2FFE363' +
'3371803868CCC768773B3C18D4D88659' +
'F46EF6799C3514E4CE6B334308125D48' +
'86964479AAC4B21885E8006B5F85099D' +
'51F6E806458F9B6CA38D0BA5975C1477',
'92 - Issuer Public Key Remainder' => '56252CC2A80775E5EFACA307422760E9' +
'DD74AF09A0B5E42BB3D86D38817DE8F1' +
'4FC66E31',
'9f32 - Issuer Public Key Exponent' => '03',
'93 - Signed Static Application Data' => '21D002319452E6E741AA4CF931A309D5' +
'2465A5E1F82FC489677EED5FF6E037C5' +
'1116B797020CBFA6EED3FBC19FF394D2' +
'B2EFD77011F777E7FC70B28FCFA4871D' +
'960BEEF3E19DA05387DD4A255783F44A' +
'23D31B6187E09A65669C63725FEC3195' +
'B963020741CDA987BB29C25A6782687F' +
'97F88C921EADC601DF8333DB79ADC0BD',
}
# XXX: use YAML or pp?
def print_hash(desc, hash)
puts desc
puts "{"
hash.each do |k,v|
puts " " + k.inspect + " => " + v.inspect + ","
end
puts "}"
end
if __FILE__ == $PROGRAM_NAME
cr = CardReader.new($card_static_var2, '../ca_keys.xml')
print_hash("Static tag", $card_static_var2)
print_hash("Ca key", cr.ca_key)
print_hash("Static info", cr.card_static_info)
print_hash("Static auth", cr.static_auth)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment