Skip to content
This repository has been archived by the owner on Sep 12, 2018. It is now read-only.

Actually implement crypto functions #4

Open
wants to merge 33 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
9d0f8c5
Implement Packet::CompressedData
singpolyma Aug 12, 2010
4b8f6bd
Newline normalizer for textual LiteralData
singpolyma Aug 12, 2010
9641c82
Uncomment Packet to_s
singpolyma Aug 12, 2010
2188810
Signature Subpackets and Trailer
singpolyma Aug 12, 2010
caa79d1
Method to get signature+data packet from message, and to do verificat…
singpolyma Aug 12, 2010
fa82f13
Implement Engine::OpenSSL::RSA
singpolyma Aug 12, 2010
47f91c1
I am a contributor
singpolyma Aug 12, 2010
895f144
Allow verify to be passed message OR key
singpolyma Aug 13, 2010
3774a03
Implement Packet::SecretKey
singpolyma Aug 13, 2010
beefa91
Improvements to byte serialization
singpolyma Aug 13, 2010
b7f4a90
Methods for creating new signatures
singpolyma Aug 13, 2010
50d9c2a
Implement OpenSSL::RSA::sign
singpolyma Aug 13, 2010
ef2c5f2
Compatability with 1.9.0
singpolyma Aug 16, 2010
521c2c7
Issuer for v3 sigs
singpolyma Aug 16, 2010
bb9f3e3
Support critical subpackets, get lengths right
singpolyma Aug 16, 2010
42accb9
Serialize issuer right
singpolyma Aug 16, 2010
e7e4757
Implement PreferredKeyServer
singpolyma Aug 16, 2010
b81de04
fix issuer search
singpolyma Aug 17, 2010
80fa278
Oops, expiration time is seconds *after* creation time
singpolyma Aug 17, 2010
8453ab5
Support leading 0s
singpolyma Jul 23, 2011
f5819e7
Implement KeyFlags
singpolyma Jul 23, 2011
330647f
Implement Features
singpolyma Jul 23, 2011
e3585bc
order hashed subpackets correctly
singpolyma Jul 23, 2011
5a08a6a
use bitlength, not *8
singpolyma Jul 23, 2011
cc5c9d6
Refactor PublicKey, Implement SecretKey
singpolyma Jul 23, 2011
54b9225
store hash_head unpacked
singpolyma Jul 23, 2011
790b27f
better exception message
singpolyma Jul 23, 2011
0932c10
UserID packet body
singpolyma Jul 23, 2011
e1bed91
Add ability to sign keys
singpolyma Jul 23, 2011
348cf5b
Add OpenSSL wrapper for signing keys
singpolyma Jul 23, 2011
fc3021a
add bn2bin and egcd algorithms
singpolyma Jul 23, 2011
cecfc66
keygen example script
singpolyma Jul 23, 2011
99a0e41
Merge branch 'keysign'
singpolyma Jul 23, 2011
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTRIBUTORS
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
* Kévin Lacointe <kevinlacointe@gmail.com> (Some GnuPG patches)
* Stephen Paul Weber <singpolyma@singpolyma.net>
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ Contributors
------------

* [Kévin Lacointe](mailto:kevinlacointe@gmail.com) - <http://github.com/klacointe>
* [Stephen Paul Weber](mailto:singpolyma@singpolyma.net) - <https://singpolyma.net>

Contributing
------------
Expand Down
17 changes: 17 additions & 0 deletions bin/keygen
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/ruby
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
require 'openpgp'

nkey = OpenSSL::PKey::RSA.new(1024)

nkey = OpenPGP::Packet::SecretKey.new(:key => nkey.params.merge({
:u => OpenPGP.egcd(nkey.params['p'].to_i, nkey.params['q'].to_i)
}).inject({}) {|c, (k, v)|
c.merge!({k.intern => OpenPGP.bn2bin(v.to_i)})
}, :algorithm => OpenPGP::Algorithm::Asymmetric::RSA, :version => 4, :timestamp => Time.now.to_i)

uid = OpenPGP::Packet::UserID.new(:name => 'Test', :email => 'test@example.com')

m = OpenPGP::Engine::OpenSSL::RSA.new(nkey).sign_key_userid([nkey, uid])

print m.to_s
8 changes: 8 additions & 0 deletions lib/openpgp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@
end
end

unless 1.respond_to?(:ord)
class Numeric
def ord
to_i
end
end
end

module OpenPGP
require 'openpgp/util'

Expand Down
166 changes: 166 additions & 0 deletions lib/openpgp/engine/openssl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,172 @@ def self.install!
[Random, Digest].each { |mod| install_extensions! mod }
end

##
# Wrap OpenSSL RSA methods for use with OpenPGP
class RSA
def initialize(packet)
packet = OpenPGP::Message::parse(packet) if packet.is_a?(String)
@key = @message = nil
if packet.is_a?(OpenPGP::Packet::PublicKey) || \
(packet.respond_to?(:first) && packet.first.is_a?(OpenPGP::Packet::PublicKey))
@key = packet
else
@message = packet
end
end

##
# @param [String] keyid (Optional)
# @return OpenPGP::Packet::PublicKey
def key(keyid=nil)
return nil unless @key
keyid.upcase! if keyid
if @key.is_a?(Enumerable) # Like an OpenPGP::Message
@key.select {|p| p.is_a?(OpenPGP::Packet::PublicKey) && (!keyid || \
p.fingerprint[keyid.length*-1,keyid.length].upcase == keyid)
}.first
end || @key
end

##
# @param [String] keyid (Optional)
# @return OpenSSL::PKey::RSA
def rsa_key(keyid=nil)
self.class.convert_key(key(keyid))
end

##
# @param packet message to verify with @key, or key (OpenPGP or RSA) to check @message with
# @param [Integer] index specify which signature to verify (if there is more than one)
# @return Boolean
def verify(packet, index=0)
packet = OpenPGP::Message::parse(packet) if packet.is_a?(String)
if packet.is_a?(OpenPGP::Message) && !packet.first.is_a?(OpenPGP::Packet::PublicKey)
m = packet
k = self
else
m = @message
k = self.class.new(packet)
end

return nil unless m
signature_packet, data_packet = m.signature_and_data(index)
k = k.rsa_key(signature_packet.issuer)
return nil unless k && signature_packet.key_algorithm_name == 'RSA'

return m.verify({'RSA' => {signature_packet.hash_algorithm_name => lambda {|m,s|
k.verify(signature_packet.hash_algorithm_name, s.first, m)
}}})
end

##
# @param packet message to sign with @key or key (OpenPGP or RSA) to sign @message with
# @param [String] hash name of hash function to use (default SHA256)
# @param [String] keyid id of key to use (if there is more than one)
# @return OpenPGP::Message
def sign(packet, hash='SHA256', keyid=nil)
packet = unless packet.is_a?(OpenPGP::Packet) || packet.is_a?(OpenPGP::Message)
if @key
OpenPGP::Packet::LiteralData.new(:data => packet, :timestamp => Time.now.to_i)
else
OpenPGP::Message::parse(packet)
end
else
packet
end

if packet.is_a?(OpenPGP::Packet::SecretKey) || packet.is_a?(::OpenSSL::PKey::RSA) \
|| (packet.is_a?(Enumerable) && packet.first.is_a?(OpenPGP::Packet::SecretKey))
m = @message
k = packet
else
m = packet
k = @key
end

return nil unless k && m # Missing some data

m = m.signature_and_data.first if m.is_a?(OpenPGP::Message)

unless k.is_a?(::OpenSSL::PKey::RSA)
k = self.class.new(k)
keyid = k.key.fingerprint[-16,16] unless keyid
k = k.rsa_key(keyid)
end

sig = OpenPGP::Packet::Signature.new(:version => 4,
:key_algorithm => OpenPGP::Algorithm::Asymmetric::RSA,
:hash_algorithm => OpenPGP::Digest::for(hash).to_i)
sig.hashed_subpackets << OpenPGP::Packet::Signature::Issuer.new(keyid)
sig.hashed_subpackets << OpenPGP::Packet::Signature::SignatureCreationTime.new(Time.now.to_i)
sig.sign_data(m, {'RSA' => {hash => lambda {|m| k.sign(hash, m)}}})
OpenPGP::Message.new([sig, m])
end

##
# @param packet message with key/userid packets to sign (@key must be set)
# @param [String] hash name of hash function to use (default SHA256)
# @param [String] keyid id of key to use (if there is more than one)
# @return OpenPGP::Message
def sign_key_userid(packet, hash='SHA256', keyid=nil)
if packet.is_a?(String)
packet = OpenPGP::Message.parse(packet)
elsif !packet.is_a?(OpenPGP::Message)
packet = OpenPGP::Message.new(packet)
end

return nil unless @key && packet # Missing some data

keyid = key.fingerprint[-16,16] unless keyid

sig = packet.signature_and_data[1]
unless sig
sig = OpenPGP::Packet::Signature.new(:version => 4,
:key_algorithm => OpenPGP::Algorithm::Asymmetric::RSA,
:hash_algorithm => OpenPGP::Digest::for(hash).to_i,
:type => 0x13)
sig.hashed_subpackets << OpenPGP::Packet::Signature::SignatureCreationTime.new(Time.now.to_i)
sig.hashed_subpackets << OpenPGP::Packet::Signature::KeyFlags.new(0x01 | 0x02)
sig.unhashed_subpackets << OpenPGP::Packet::Signature::Issuer.new(keyid)
packet << sig
end
sig.sign_data(packet, {'RSA' => {hash => lambda {|m| rsa_key.sign(hash, m)}}})

packet
end

##
# @param packet
# @return [OpenSSL::PKey::RSA]
def self.convert_key(packet)
# packet is already an key
return packet if packet.is_a?(::OpenSSL::PKey::RSA)
unless packet.is_a?(Hash)
# Get the first item in a message
packet = packet.first if packet.is_a?(Enumerable)
# TODO: Error if packet.algorithm not RSA
packet = packet.key # Get key material
end

# Create blank key and fill the fields
key = ::OpenSSL::PKey::RSA.new
packet.each {|k,v|
next if k == :u # OpenSSL doesn't call it that
if v.is_a?(Numeric)
v = ::OpenSSL::BN.new(v.to_s)
elsif !(v.is_a?(::OpenSSL::BN))
# Convert the byte string to an OpenSSL::BN
v = v.reverse.enum_for(:each_char).enum_for(:each_with_index) \
.inject(::OpenSSL::BN.new('0')) {|c, (b,i)|
c + (b.force_encoding('binary').ord << i*8)
}
end
key.send("#{k}=".intern, v)
}
key
end
end

##
# @private
module Random #:nodoc:
Expand Down
35 changes: 30 additions & 5 deletions lib/openpgp/message.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,35 @@ def initialize(*packets, &block)
block.call(self) if block_given?
end

def signature_and_data(index=0)
msg = self
msg = msg.first while msg.first.is_a?(OpenPGP::Packet::CompressedData)
signature_packet = data_packet = nil
i = 0
msg.each { |packet|
if packet.is_a?(OpenPGP::Packet::Signature)
signature_packet = packet if i == index
i += 1
elsif packet.is_a?(OpenPGP::Packet::LiteralData)
data_packet = packet
end
break if signature_packet && data_packet
}
[signature_packet, data_packet]
end

##
# @param verifiers a Hash of callables formatted like {'RSA' => {'SHA256' => callable}} that take two parameters: message and signature
# @param index signature number to verify (if more than one)
def verify(verifiers, index=0)
signature_packet, data_packet = signature_and_data(index)
return nil unless signature_packet && data_packet # No signature or no data
verifier = verifiers[signature_packet.key_algorithm_name][signature_packet.hash_algorithm_name]
return nil unless verifier # No verifier
data_packet.normalize
verifier.call(data_packet.data + signature_packet.trailer, signature_packet.fields)
end

##
# @yield [packet]
# @yieldparam [Packet] packet
Expand Down Expand Up @@ -124,11 +153,7 @@ def size
def to_s
Buffer.write do |buffer|
packets.each do |packet|
if body = packet.body
buffer.write_byte(packet.class.tag | 0xC0)
buffer.write_byte(body.size)
buffer.write_bytes(body)
end
buffer.write_bytes(packet.to_s)
end
end
end
Expand Down
Loading