Class: CryptoToolchain::BlackBoxes::RSAKeypair

Inherits:
Object
  • Object
show all
Defined in:
lib/crypto_toolchain/black_boxes/rsa_keypair.rb

Defined Under Namespace

Classes: PrivateKey, PublicKey

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(bits: 1024, p: nil, q: nil) ⇒ RSAKeypair

Returns a new instance of RSAKeypair.



8
9
10
11
12
13
14
15
16
# File 'lib/crypto_toolchain/black_boxes/rsa_keypair.rb', line 8

def initialize(bits: 1024, p: nil, q: nil)
  @bits = bits
  @p = p || OpenSSL::BN::generate_prime(bits/2).to_i
  @q = q || OpenSSL::BN::generate_prime(bits/2).to_i
  @n = @p * @q
  et = (@p-1) * (@q-1)
  @e = 3
  @d = @e.invmod(et)
end

Instance Attribute Details

#bitsObject (readonly)

Returns the value of attribute bits.



18
19
20
# File 'lib/crypto_toolchain/black_boxes/rsa_keypair.rb', line 18

def bits
  @bits
end

#eObject (readonly)

Returns the value of attribute e.



18
19
20
# File 'lib/crypto_toolchain/black_boxes/rsa_keypair.rb', line 18

def e
  @e
end

#pObject (readonly)

Returns the value of attribute p.



18
19
20
# File 'lib/crypto_toolchain/black_boxes/rsa_keypair.rb', line 18

def p
  @p
end

#qObject (readonly)

Returns the value of attribute q.



18
19
20
# File 'lib/crypto_toolchain/black_boxes/rsa_keypair.rb', line 18

def q
  @q
end

Instance Method Details

#asn1(hash_type) ⇒ Object

Raises:

  • (ArgumentError)


76
77
78
79
80
81
82
83
84
85
# File 'lib/crypto_toolchain/black_boxes/rsa_keypair.rb', line 76

def asn1(hash_type)
  raise ArgumentError.new("Only sha1 is supported") unless hash_type == :sha1
  {
    md5:    "0 0\f\x06\b*\x86H\x86\xF7\r\x02\x05\x05\x00\x04\x10",
    sha1:   "0!0\t\x06\x05+\x0E\x03\x02\x1A\x05\x00\x04\x14",
    sha256: "010\r\x06\t`\x86H\x01e\x03\x04\x02\x01\x05\x00\x04 ",
    sha384: "0A0\r\x06\t`\x86H\x01e\x03\x04\x02\x02\x05\x00\x040",
    sha512: "0Q0\r\x06\t`\x86H\x01e\x03\x04\x02\x03\x05\x00\x04@"
  }.fetch(hash_type)
end

#decrypt(m, pad: false) ⇒ Object

Raises:

  • (ArgumentError)


28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/crypto_toolchain/black_boxes/rsa_keypair.rb', line 28

def decrypt(m, pad: false)
  raise ArgumentError.new("Message should be a string") unless m.is_a?(String)
  decrypted = m.
                to_number.
                modpow(private_key.d, private_key.n).
                to_bin_string
  if pad
    pad_to_key_size(decrypted)
  else
    decrypted
  end
end

#encrypt(m, to:) ⇒ Object

Raises:

  • (ArgumentError)


20
21
22
23
24
25
26
# File 'lib/crypto_toolchain/black_boxes/rsa_keypair.rb', line 20

def encrypt(m, to: )
  raise ArgumentError.new("Message should be a string") unless m.is_a?(String)
  m.
    to_number.
    modpow(to.e, to.n).
    to_bin_string
end

#pad_to_key_size(str) ⇒ Object



87
88
89
90
91
92
93
# File 'lib/crypto_toolchain/black_boxes/rsa_keypair.rb', line 87

def pad_to_key_size(str)
  padded = str.dup
  while padded.bytesize < bits / 8
    padded = "\x00#{padded}"
  end
  padded
end

#private_keyObject Also known as: privkey



69
70
71
# File 'lib/crypto_toolchain/black_boxes/rsa_keypair.rb', line 69

def private_key
  @private_key ||= PrivateKey.new(@d, @n)
end

#public_keyObject Also known as: pubkey



64
65
66
# File 'lib/crypto_toolchain/black_boxes/rsa_keypair.rb', line 64

def public_key
  @public_key ||= PublicKey.new(@e, @n)
end

#sign(plaintext) ⇒ Object



41
42
43
44
45
46
47
48
49
# File 'lib/crypto_toolchain/black_boxes/rsa_keypair.rb', line 41

def sign(plaintext)
  blocksize = bits / 8
  digest = CryptoToolchain::Utilities::SHA1.digest(plaintext)
  asn = asn1(:sha1)
  # the 4 is the mandatory 0x00 0x01 0xff 0x00
  pad_num = blocksize - ( digest.bytesize + asn.bytesize + 4)
  block = "\x00\x01\xff" + (0xff.chr * pad_num) + "\x00" + asn + digest
  decrypt(block)
end

#verify(message, signature:, lazy: true) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/crypto_toolchain/black_boxes/rsa_keypair.rb', line 51

def verify(message, signature: , lazy: true)
  raise("I can't not be lazy") unless lazy
  enc = encrypt(signature, to: pubkey)
  asn = ASN1.fetch(:sha1)
  regex = /(?<padding>\x01\xff+\x00)(?<asn>.{#{asn.bytesize}})(?<hash>.{20})/m
  begin
    _padding, potential_asn, hash = enc.match(regex).captures
  rescue
    return false
  end
  potential_asn == asn && hash == CryptoToolchain::Utilities::SHA1.digest(message)
end