Skip to content
This repository has been archived by the owner on Nov 15, 2019. It is now read-only.

Commit

Permalink
Initial Gem::TUF::Signer
Browse files Browse the repository at this point in the history
  • Loading branch information
Tony Arcieri committed Nov 19, 2013
1 parent f30f48e commit 09697a9
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 0 deletions.
32 changes: 32 additions & 0 deletions lib/rubygems/tuf.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
module Gem::TUF
##
# Digest algorithm used to sign gems

DIGEST_ALGORITHM =
if defined?(OpenSSL::Digest) then
OpenSSL::Digest::SHA512
end

##
# Used internally to select the signing digest from all computed digests

DIGEST_NAME = # :nodoc:
if DIGEST_ALGORITHM then
DIGEST_ALGORITHM.new.name
end

##
# Algorithm for creating the key pair used to sign gems

KEY_ALGORITHM =
if defined?(OpenSSL::PKey) then
OpenSSL::PKey::RSA
end

##
# Length of keys created by KEY_ALGORITHM

KEY_LENGTH = 2048
end

require 'rubygems/tuf/signer'
37 changes: 37 additions & 0 deletions lib/rubygems/tuf/signer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# TODO: remove this dependency somehow
require 'json'

##
# Produce signed JSON documents in The Update Framework (TUF) format

class Gem::TUF::Signer
def initialize key
raise TypeError, "expecting a #{KEY_ALGORITHM}" unless key.is_a? Gem::TUF::KEY_ALGORITHM
@key = key
end

def sign json
signed = json['signed']
raise ArgumentError, "no data to sign" unless signed
raise ArgumentError, "expected 'signed' to contain '_type'" unless signed['_type']

# TODO: canonical JSON
#to_sign = CanonicalJSON.dump(signed)
to_sign = JSON.dump(signed)

signature = @key.sign(Gem::TUF::DIGEST_ALGORITHM.new, to_sign)

signatures = json['signatures'] ||= []
signatures << {

This comment has been minimized.

Copy link
@xaviershay

xaviershay Nov 19, 2013

should be a check here to not add duplicate entries for the same key id (i.e. make this method idempotent). Probably want to raise if the digests are different.

This comment has been minimized.

Copy link
@tarcieri

tarcieri Nov 20, 2013

Yeah, definitely

"keyid" => keyid,
"method" => "RSASSA-PKCS#1-v1.5+SHA512",
"sig" => signature.unpack("H*").first
}

json
end

def keyid
Digest::SHA256.hexdigest(@key.public_key.to_der)
end
end
52 changes: 52 additions & 0 deletions test/rubygems/test_gem_tuf_signer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
require 'rubygems/test_case'
require 'rubygems/tuf'

unless defined?(OpenSSL::SSL) then
warn 'Skipping Gem::TUF::Signer tests. openssl not found.'
end

class TestGemTUFSigner < Gem::TestCase

ALTERNATE_KEY = load_key 'alternate'
CHILD_KEY = load_key 'child'
GRANDCHILD_KEY = load_key 'grandchild'

CHILD_CERT = load_cert 'child'
GRANDCHILD_CERT = load_cert 'grandchild'
EXPIRED_CERT = load_cert 'expired'

def setup
super

@cert_file = PUBLIC_CERT
@signable = {
"signed" => {
"_type" => "Example",
"version" => 1
}
}
end

def test_initialize
Gem::TUF::Signer.new PRIVATE_KEY
end

def test_sign
signer = Gem::TUF::Signer.new PRIVATE_KEY
signed = signer.sign @signable

# TODO: get real TUF test vectors
expected = "5c628a8be727808d1db495a82dd210fc014054696c940ff628dc47e4e2d6" +
"86947934c35fb694b195dc23cdca25d1fa2a758f9da92d224cbd0d47baa1" +
"23490d85cacbfe607a5fa0ea5251480d087b7390ecfb43326d03c395ef85" +
"1f3ba17cbb5ff8e759b90d5edcce55aa1b19611641cc347725327e846481" +
"04b06a69d6b4859f37843495a5096d86b56389db2a795e5abdcdb70511b3" +
"c871b7f3f1ff39a204247f358e754e39d318bd767fdd9ede4fcd9a8e1cb6" +
"aa67f42b6aa6b27087b6635d49ba05dcdc509ae0cc9754d82cdd13a784c9" +
"e28d347bed47a3810934fa5be7971364ed71a7fca996d2acbf80d6f8798a" +
"bc2c3f7d80dba1c03cb20d52a008f9f9"

assert_equal expected, signed['signatures'].first['sig']
end
end if defined?(OpenSSL::SSL)

0 comments on commit 09697a9

Please sign in to comment.