summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoro <o@immerda.ch>2011-06-25 17:54:45 +0200
committero <o@immerda.ch>2011-06-25 17:54:45 +0200
commitc2eae6fa1ee87599307321b9d432e4148f41c228 (patch)
tree9d2b39250a250d9c47aaa2c3238be6bce446086b
initial commit. there is already a radix64 implementation
-rw-r--r--.bnsignore18
-rw-r--r--.gitignore0
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock18
-rw-r--r--History.txt4
-rw-r--r--README.md60
-rw-r--r--Rakefile10
-rwxr-xr-xbin/fpg7
-rw-r--r--fpg.gemspec20
-rw-r--r--lib/fpg.rb60
-rw-r--r--lib/fpg/io/radix64.rb104
-rw-r--r--lib/fpg/version.rb13
-rw-r--r--spec/fixtures/test.bare.pub47
-rw-r--r--spec/fixtures/test.bare.sec102
-rw-r--r--spec/fixtures/test.bin.pubbin0 -> 2209 bytes
-rw-r--r--spec/fixtures/test.bin.secbin0 -> 4867 bytes
-rw-r--r--spec/fixtures/test.pub52
-rw-r--r--spec/fixtures/test.sec107
-rw-r--r--spec/fpg_spec.rb6
-rw-r--r--spec/radix64_spec.rb60
-rw-r--r--spec/spec_helper.rb7
-rw-r--r--tasks/rspec.rake2
-rw-r--r--test/test_fpg.rb0
-rw-r--r--version.txt1
24 files changed, 700 insertions, 0 deletions
diff --git a/.bnsignore b/.bnsignore
new file mode 100644
index 0000000..5fa69e9
--- /dev/null
+++ b/.bnsignore
@@ -0,0 +1,18 @@
+# The list of files that should be ignored by Mr Bones.
+# Lines that start with '#' are comments.
+#
+# A .gitignore file can be used instead by setting it as the ignore
+# file in your Rakefile:
+#
+# Bones {
+# ignore_file '.gitignore'
+# }
+#
+# For a project with a C extension, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
+announcement.txt
+coverage
+doc
+pkg
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/.gitignore
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..a8a9268
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,2 @@
+source 'http://rubygems.org'
+gem 'rspec'
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 0000000..32a4b81
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,18 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ diff-lcs (1.1.2)
+ rspec (2.6.0)
+ rspec-core (~> 2.6.0)
+ rspec-expectations (~> 2.6.0)
+ rspec-mocks (~> 2.6.0)
+ rspec-core (2.6.4)
+ rspec-expectations (2.6.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.6.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ rspec
diff --git a/History.txt b/History.txt
new file mode 100644
index 0000000..42e88b7
--- /dev/null
+++ b/History.txt
@@ -0,0 +1,4 @@
+== 1.0.0 / 2011-06-25
+
+* 1 major enhancement
+ * Birthday!
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a9ec85f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,60 @@
+fpg
+===========
+
+FIXME (describe your package)
+
+Features
+--------
+
+* FIXME (list of features and unsolved problems)
+
+Examples
+--------
+
+ FIXME (code sample of usage)
+
+Requirements
+------------
+
+* FIXME (list of requirements)
+
+Install
+-------
+
+* FIXME (sudo gem install, anything else)
+
+Author
+------
+
+Original author: FIXME (author's name)
+
+Contributors:
+
+* FIXME (contributor 1?)
+* FIXME (contributor 2?)
+
+License
+-------
+
+(The MIT License) FIXME (different license?)
+
+Copyright (c) 2011 FIXME (author's name)
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..9229c7d
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,10 @@
+require "rake/gempackagetask"
+require "rake/clean"
+
+CLEAN << "pkg" << "doc" << "coverage"
+task :default => [:rspec]
+
+Dir['tasks/**/*.rake'].each { |t| load t }
+
+Rake::GemPackageTask.new(eval(File.read("fpg.gemspec"))) { |pkg| }
+
diff --git a/bin/fpg b/bin/fpg
new file mode 100755
index 0000000..db09e33
--- /dev/null
+++ b/bin/fpg
@@ -0,0 +1,7 @@
+#!/usr/bin/env ruby
+
+require File.expand_path(
+ File.join(File.dirname(__FILE__), %w[.. lib fpg]))
+
+# Put your code here
+
diff --git a/fpg.gemspec b/fpg.gemspec
new file mode 100644
index 0000000..0664086
--- /dev/null
+++ b/fpg.gemspec
@@ -0,0 +1,20 @@
+# encoding: utf-8
+
+$: << File.expand_path('../lib', __FILE__)
+
+Gem::Specification.new do |s|
+ s.name = "fpg"
+ s.version = 0
+ s.authors = []
+ s.email = "immerda.ch"
+ s.homepage = "immerda.ch"
+ s.summary = ""
+ s.description = ""
+
+
+ s.files = `git ls-files lib`.split("\n") + %w(README.textile CHANGELOG.textile man/arver.5)
+ s.executables = [ 'fpg' ]
+ s.platform = Gem::Platform::RUBY
+ s.require_path = 'lib'
+ s.rubyforge_project = '[none]'
+end
diff --git a/lib/fpg.rb b/lib/fpg.rb
new file mode 100644
index 0000000..eca9897
--- /dev/null
+++ b/lib/fpg.rb
@@ -0,0 +1,60 @@
+
+module Fpg
+
+ # :stopdoc:
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
+ VERSION = ::File.read(PATH + 'version.txt').strip
+ # :startdoc:
+
+ # Returns the library path for the module. If any arguments are given,
+ # they will be joined to the end of the libray path using
+ # <tt>File.join</tt>.
+ #
+ def self.libpath( *args )
+ rv = args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
+ if block_given?
+ begin
+ $LOAD_PATH.unshift LIBPATH
+ rv = yield
+ ensure
+ $LOAD_PATH.shift
+ end
+ end
+ return rv
+ end
+
+ # Returns the lpath for the module. If any arguments are given,
+ # they will be joined to the end of the path using
+ # <tt>File.join</tt>.
+ #
+ def self.path( *args )
+ rv = args.empty? ? PATH : ::File.join(PATH, args.flatten)
+ if block_given?
+ begin
+ $LOAD_PATH.unshift PATH
+ rv = yield
+ ensure
+ $LOAD_PATH.shift
+ end
+ end
+ return rv
+ end
+
+ # Utility method used to require all files ending in .rb that lie in the
+ # directory below this file that has the same name as the filename passed
+ # in. Optionally, a specific _directory_ name can be passed in such that
+ # the _filename_ does not have to be equivalent to the directory.
+ #
+ def self.require_all_libs_relative_to( fname, dir = nil )
+ dir ||= ::File.basename(fname, '.*')
+ search_me = ::File.expand_path(
+ ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
+
+ Dir.glob(search_me).sort.each {|rb| require rb}
+ end
+
+end # module Fpg
+
+Fpg.require_all_libs_relative_to(__FILE__)
+
diff --git a/lib/fpg/io/radix64.rb b/lib/fpg/io/radix64.rb
new file mode 100644
index 0000000..49102a0
--- /dev/null
+++ b/lib/fpg/io/radix64.rb
@@ -0,0 +1,104 @@
+module FPG
+ class Radix64
+ def char_table
+ @char_table ||= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".each_byte.collect.to_a
+ end
+ def pad
+ pad_s.unpack("C1!").first
+ end
+ def pad_s
+ "="
+ end
+ def char2six_table
+ @char2six_table ||= Hash[char_table.zip((0..63).to_a)]
+ end
+ def char2six(char)
+ char2six_table[char]
+ end
+ def six2char(six)
+ char_table[six]
+ end
+ def quad2bytes(quad)
+ q = (quad.unpack("C4!").collect { |v| char2six(v) }).to_a
+ [ (q[0]<<2)+(q[1]>>4), ((q[1]<<4)%256)+(q[2]>>2), ((q[2]<<6)%256)+q[3] ]
+ end
+ def padded_quad2bytes(quad)
+ if quad[2] == pad_s then
+ q = quad.unpack("C2!").collect { |v| char2six(v) }
+ [ (q[0]<<2)+(q[1]>>4), ((q[1]<<4)%256)>>4 ]
+ else
+ q = quad.unpack("C3!").collect { |v| char2six(v) }
+ [ (q[0]<<2)+(q[1]>>4), ((q[1]<<4)%256)+(q[2]>>2), ((q[2]<<6)%256) ]
+ end
+ end
+ def bytes2quad(bytes)
+ [ six2char(bytes[0]>>2), six2char(((bytes[0]<<4)%64)+(bytes[1]>>4)),
+ six2char(((bytes[1]<<2)%64)+(bytes[2]>>6)), six2char(bytes[2]%64) ].pack("C4!")
+ end
+ def bytes2padded_quad(bytes)
+ if bytes.length == 1
+ [six2char(bytes[0]>>2), six2char((bytes[0]<<4)%64), pad, pad].pack("C4!")
+ elsif bytes.length == 2
+ [ six2char(bytes[0]>>2), six2char(((bytes[0]<<4)%64)+(bytes[1]>>4)),
+ six2char((bytes[1]<<2)%64), pad].pack("C4!")
+ end
+ end
+ def de_armor( stream )
+ stream.each do |line|
+ line.chop!
+ return if line.length == 0
+ 0.step(line.length-1, 4) do |i|
+ quad = line[i..i+3]
+ if quad[3] == pad_s
+ bytes = padded_quad2bytes(quad)
+ if bytes.length == 2 and bytes[1] != 0
+ yield bytes[0]
+ yield bytes[1]
+ elsif bytes[0] != 0
+ yield bytes[0]
+ end
+ return
+ end
+ bytes = quad2bytes(quad)
+ yield bytes[0]
+ yield bytes[1]
+ yield bytes[2]
+ end
+ end
+ end
+ def armor( stream )
+ line_buffer = ""
+ line_length = 0
+ buffer = [0,0,0]
+ buffer_size = 0
+ stream.each_byte do |byte|
+ if buffer_size == 3 then
+ line_buffer << bytes2quad(buffer)
+ line_length += 4
+ buffer_size = 0
+ end
+ if line_length > 63 then
+ yield line_buffer
+ line_length = 0
+ line_buffer = ""
+ end
+ buffer[buffer_size] = byte
+ buffer_size += 1
+ end
+ if buffer_size == 1
+ line_buffer << bytes2padded_quad([buffer[0]])
+ elsif buffer_size == 2
+ line_buffer << bytes2padded_quad([buffer[0],buffer[1]])
+ end
+ yield line_buffer
+ end
+ def armor_public_key( stream )
+ yield Versioning.public_key_header
+ armor( stream )
+ yield Versioning.public_key_footer
+ end
+ end
+end
+
+
+
diff --git a/lib/fpg/version.rb b/lib/fpg/version.rb
new file mode 100644
index 0000000..dc66bf0
--- /dev/null
+++ b/lib/fpg/version.rb
@@ -0,0 +1,13 @@
+module FPG
+ class Versioning
+ def self.public_key_header
+ "-----BEGIN FGP PUBLIC KEY BLOCK-----\n#{version_info}\n"
+ end
+ def self.public_key_footer
+ "-----END FGP PUBLIC KEY BLOCK-----\n"
+ end
+ def self.version_info
+ "Version: Friendlier Privacy Guard 0.0.0\n"
+ end
+ end
+end
diff --git a/spec/fixtures/test.bare.pub b/spec/fixtures/test.bare.pub
new file mode 100644
index 0000000..160f4c6
--- /dev/null
+++ b/spec/fixtures/test.bare.pub
@@ -0,0 +1,47 @@
+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
+cA==
diff --git a/spec/fixtures/test.bare.sec b/spec/fixtures/test.bare.sec
new file mode 100644
index 0000000..f40df2c
--- /dev/null
+++ b/spec/fixtures/test.bare.sec
@@ -0,0 +1,102 @@
+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
+vAeewnmtxdhxtZWnASv6gM72cA==
diff --git a/spec/fixtures/test.bin.pub b/spec/fixtures/test.bin.pub
new file mode 100644
index 0000000..e7b2393
--- /dev/null
+++ b/spec/fixtures/test.bin.pub
Binary files differ
diff --git a/spec/fixtures/test.bin.sec b/spec/fixtures/test.bin.sec
new file mode 100644
index 0000000..7ed3cca
--- /dev/null
+++ b/spec/fixtures/test.bin.sec
Binary files differ
diff --git a/spec/fixtures/test.pub b/spec/fixtures/test.pub
new file mode 100644
index 0000000..e339e0d
--- /dev/null
+++ b/spec/fixtures/test.pub
@@ -0,0 +1,52 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+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
+cA==
+=Y5tE
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/spec/fixtures/test.sec b/spec/fixtures/test.sec
new file mode 100644
index 0000000..0827bb9
--- /dev/null
+++ b/spec/fixtures/test.sec
@@ -0,0 +1,107 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+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
+vAeewnmtxdhxtZWnASv6gM72cA==
+=9toC
+-----END PGP PRIVATE KEY BLOCK-----
diff --git a/spec/fpg_spec.rb b/spec/fpg_spec.rb
new file mode 100644
index 0000000..6dbc295
--- /dev/null
+++ b/spec/fpg_spec.rb
@@ -0,0 +1,6 @@
+
+require File.join(File.dirname(__FILE__), %w[spec_helper])
+
+describe Fpg do
+end
+
diff --git a/spec/radix64_spec.rb b/spec/radix64_spec.rb
new file mode 100644
index 0000000..73262d1
--- /dev/null
+++ b/spec/radix64_spec.rb
@@ -0,0 +1,60 @@
+
+require File.join(File.dirname(__FILE__), %w[spec_helper])
+
+require 'stringio'
+
+describe Radix64 do
+ before(:each) do
+ @radix = Radix64.new
+ @binary_pub = File.open "spec/fixtures/test.bin.pub", 'rb'
+ @armored_pub = File.open "spec/fixtures/test.bare.pub", 'r'
+ @binary_sec = File.open "spec/fixtures/test.bin.sec", 'rb'
+ @armored_sec = File.open "spec/fixtures/test.bare.sec", 'r'
+ @in = ""
+ @in2 = ""
+ end
+
+ it "should correctly armor a binary public key" do
+ @radix.armor( @binary_pub ) { |line| @in << line << "\n" }
+ @armored_pub.read.should == @in
+ end
+ it "should correctly armor a binary private key" do
+ @radix.armor( @binary_sec ) { |line| @in << line << "\n" }
+ @armored_sec.read.should == @in
+ end
+ it "should correctly dearmor a public key" do
+ @radix.de_armor( @armored_pub ) { |byte| @in << [byte].pack("C1!") }
+ @binary_pub.read.should == @in
+ end
+ it "should de/-armor to the same key" do
+ @radix.de_armor( @armored_pub ) { |byte| @in << byte }
+ @radix.armor( @in ) { |line| @in2 << line << "\n" }
+ @armored_pub.rewind
+ @armored_pub.read.should == @in2
+ end
+ it "should armor/dearmor to the same key" do
+ in_str = StringIO.new(@in)
+ @radix.armor( @binary_sec ) { |line| in_str << line }
+ in_str << "\n"
+ in_str.rewind
+ @radix.de_armor( in_str ) { |byte| @in2 << [byte].pack("C1!") }
+ @binary_sec.rewind
+ @binary_sec.read.should == @in2
+ end
+ it "should stay invariant under de-/armoring" do
+ first = ""
+ @radix.armor( @binary_pub ) { |line| first << line << "\n" }
+
+ in_str = StringIO.new(first.clone)
+ 10.times do
+ in_str.rewind
+ @in = ""
+ @radix.de_armor( in_str ) { |byte| @in << byte }
+ in_str.rewind
+ @radix.armor( @in ) { |line| in_str << line << "\n" }
+ end
+ in_str.rewind
+ in_str.read.should == first
+ end
+end
+
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
new file mode 100644
index 0000000..a435f1d
--- /dev/null
+++ b/spec/spec_helper.rb
@@ -0,0 +1,7 @@
+require File.expand_path(File.join(File.dirname(__FILE__), %w[.. lib fpg]))
+
+include FPG
+
+RSpec.configure do |config|
+end
+
diff --git a/tasks/rspec.rake b/tasks/rspec.rake
new file mode 100644
index 0000000..c2a4ce4
--- /dev/null
+++ b/tasks/rspec.rake
@@ -0,0 +1,2 @@
+require 'rspec/core/rake_task'
+RSpec::Core::RakeTask.new(:rspec)
diff --git a/test/test_fpg.rb b/test/test_fpg.rb
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/test_fpg.rb
diff --git a/version.txt b/version.txt
new file mode 100644
index 0000000..77d6f4c
--- /dev/null
+++ b/version.txt
@@ -0,0 +1 @@
+0.0.0