ppp 0.1.2.alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +26 -0
- data/ext/ppp/cppp.c +79 -0
- data/ext/ppp/extconf.rb +5 -0
- data/ext/ppp/ppp.c +253 -0
- data/ext/ppp/ppp.h +44 -0
- data/ext/ppp/rijndael.c +1206 -0
- data/ext/ppp/rijndael.h +18 -0
- data/ext/ppp/sha2.c +950 -0
- data/ext/ppp/sha2.h +108 -0
- data/lib/ppp.rb +41 -0
- data/lib/ppp/card/base.rb +50 -0
- data/lib/ppp/card/html.rb +126 -0
- data/lib/ppp/card/plain.rb +91 -0
- data/lib/ppp/generator.rb +69 -0
- data/lib/ppp/version.rb +3 -0
- metadata +73 -0
data/ext/ppp/sha2.h
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
/*
|
2
|
+
* FIPS 180-2 SHA-224/256/384/512 implementation
|
3
|
+
* Last update: 02/02/2007
|
4
|
+
* Issue date: 04/30/2005
|
5
|
+
*
|
6
|
+
* Copyright (C) 2005, 2007 Olivier Gay <olivier.gay@a3.epfl.ch>
|
7
|
+
* All rights reserved.
|
8
|
+
*
|
9
|
+
* Redistribution and use in source and binary forms, with or without
|
10
|
+
* modification, are permitted provided that the following conditions
|
11
|
+
* are met:
|
12
|
+
* 1. Redistributions of source code must retain the above copyright
|
13
|
+
* notice, this list of conditions and the following disclaimer.
|
14
|
+
* 2. Redistributions in binary form must reproduce the above copyright
|
15
|
+
* notice, this list of conditions and the following disclaimer in the
|
16
|
+
* documentation and/or other materials provided with the distribution.
|
17
|
+
* 3. Neither the name of the project nor the names of its contributors
|
18
|
+
* may be used to endorse or promote products derived from this software
|
19
|
+
* without specific prior written permission.
|
20
|
+
*
|
21
|
+
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
|
22
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
23
|
+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
24
|
+
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
|
25
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
26
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
27
|
+
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
28
|
+
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
29
|
+
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
30
|
+
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
31
|
+
* SUCH DAMAGE.
|
32
|
+
*/
|
33
|
+
|
34
|
+
#ifndef SHA2_H
|
35
|
+
#define SHA2_H
|
36
|
+
|
37
|
+
#define SHA224_DIGEST_SIZE ( 224 / 8)
|
38
|
+
#define SHA256_DIGEST_SIZE ( 256 / 8)
|
39
|
+
#define SHA384_DIGEST_SIZE ( 384 / 8)
|
40
|
+
#define SHA512_DIGEST_SIZE ( 512 / 8)
|
41
|
+
|
42
|
+
#define SHA256_BLOCK_SIZE ( 512 / 8)
|
43
|
+
#define SHA512_BLOCK_SIZE (1024 / 8)
|
44
|
+
#define SHA384_BLOCK_SIZE SHA512_BLOCK_SIZE
|
45
|
+
#define SHA224_BLOCK_SIZE SHA256_BLOCK_SIZE
|
46
|
+
|
47
|
+
#ifndef SHA2_TYPES
|
48
|
+
#define SHA2_TYPES
|
49
|
+
typedef unsigned char uint8;
|
50
|
+
typedef unsigned int uint32;
|
51
|
+
typedef unsigned long long uint64;
|
52
|
+
#endif
|
53
|
+
|
54
|
+
#ifdef __cplusplus
|
55
|
+
extern "C" {
|
56
|
+
#endif
|
57
|
+
|
58
|
+
typedef struct {
|
59
|
+
unsigned int tot_len;
|
60
|
+
unsigned int len;
|
61
|
+
unsigned char block[2 * SHA256_BLOCK_SIZE];
|
62
|
+
uint32 h[8];
|
63
|
+
} sha256_ctx;
|
64
|
+
|
65
|
+
typedef struct {
|
66
|
+
unsigned int tot_len;
|
67
|
+
unsigned int len;
|
68
|
+
unsigned char block[2 * SHA512_BLOCK_SIZE];
|
69
|
+
uint64 h[8];
|
70
|
+
} sha512_ctx;
|
71
|
+
|
72
|
+
typedef sha512_ctx sha384_ctx;
|
73
|
+
typedef sha256_ctx sha224_ctx;
|
74
|
+
|
75
|
+
void sha224_init(sha224_ctx *ctx);
|
76
|
+
void sha224_update(sha224_ctx *ctx, const unsigned char *message,
|
77
|
+
unsigned int len);
|
78
|
+
void sha224_final(sha224_ctx *ctx, unsigned char *digest);
|
79
|
+
void sha224(const unsigned char *message, unsigned int len,
|
80
|
+
unsigned char *digest);
|
81
|
+
|
82
|
+
void sha256_init(sha256_ctx * ctx);
|
83
|
+
void sha256_update(sha256_ctx *ctx, const unsigned char *message,
|
84
|
+
unsigned int len);
|
85
|
+
void sha256_final(sha256_ctx *ctx, unsigned char *digest);
|
86
|
+
void sha256(const unsigned char *message, unsigned int len,
|
87
|
+
unsigned char *digest);
|
88
|
+
|
89
|
+
void sha384_init(sha384_ctx *ctx);
|
90
|
+
void sha384_update(sha384_ctx *ctx, const unsigned char *message,
|
91
|
+
unsigned int len);
|
92
|
+
void sha384_final(sha384_ctx *ctx, unsigned char *digest);
|
93
|
+
void sha384(const unsigned char *message, unsigned int len,
|
94
|
+
unsigned char *digest);
|
95
|
+
|
96
|
+
void sha512_init(sha512_ctx *ctx);
|
97
|
+
void sha512_update(sha512_ctx *ctx, const unsigned char *message,
|
98
|
+
unsigned int len);
|
99
|
+
void sha512_final(sha512_ctx *ctx, unsigned char *digest);
|
100
|
+
void sha512(const unsigned char *message, unsigned int len,
|
101
|
+
unsigned char *digest);
|
102
|
+
|
103
|
+
#ifdef __cplusplus
|
104
|
+
}
|
105
|
+
#endif
|
106
|
+
|
107
|
+
#endif /* !SHA2_H */
|
108
|
+
|
data/lib/ppp.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'digest'
|
2
|
+
require "ppp/version"
|
3
|
+
require "ppp/generator"
|
4
|
+
require 'ppp/Cppp'
|
5
|
+
require 'ppp/card/base'
|
6
|
+
require 'ppp/card/html'
|
7
|
+
require 'ppp/card/plain'
|
8
|
+
|
9
|
+
module Ppp
|
10
|
+
class << self
|
11
|
+
@@ALPHABETS = { :conservative => '!#%+23456789:=?@ABCDEFGHJKLMNPRSTUVWXYZabcdefghijkmnopqrstuvwxyz',
|
12
|
+
:aggressive => '!"#$%&\'()*+,-./23456789:;<=>?@ABCDEFGHJKLMNOPRSTUVWXYZ[\]^_abcdefghijkmnopqrstuvwxyz{|}~' }
|
13
|
+
|
14
|
+
# @return [Ppp::Generator] with the given SHA-256 key
|
15
|
+
def code_generator key, opts
|
16
|
+
Generator.new key, opts
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return a SHA-256 digest of the given string
|
20
|
+
def key_from_string str
|
21
|
+
Digest::SHA256.new.update( str ).to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return a random SHA-256 key
|
25
|
+
def random_key
|
26
|
+
Cppp.random_key
|
27
|
+
end
|
28
|
+
|
29
|
+
def printer style, *args
|
30
|
+
case style
|
31
|
+
when :html then return Card::Html.new *args
|
32
|
+
when :plain then return Card::Plain.new *args
|
33
|
+
end
|
34
|
+
raise ArgumentError.new( "%s is not a valid printer style." % style )
|
35
|
+
end
|
36
|
+
|
37
|
+
def default_alphabets
|
38
|
+
@@ALPHABETS
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'ppp/generator'
|
2
|
+
|
3
|
+
module Ppp
|
4
|
+
module Card
|
5
|
+
class Base
|
6
|
+
@@CHARS_PER_LINE = 34
|
7
|
+
@@FIRST_COLUMN = ?A
|
8
|
+
@@ROW_COL_PATTERN = /[[:digit:]][[:alpha:]]/
|
9
|
+
|
10
|
+
@@ERROR_BAD_ROW_COL = %[Expected a string with exactly one digit and one letter, got "%s".]
|
11
|
+
@@ERROR_LONG_CODES = %[Passcodes longer than 16 characters are too long for printing]
|
12
|
+
|
13
|
+
def initialize generator, opts={}
|
14
|
+
@generator = generator
|
15
|
+
raise ArgumentError.new( @@ERROR_LONG_CODES ) if code_length > 16
|
16
|
+
|
17
|
+
options = { :card_title => 'PPP Passcard', :first_card_index => 1 }.merge opts
|
18
|
+
@title = options[ :card_title ]
|
19
|
+
@card_index = options[ :first_card_index ]
|
20
|
+
@offset = 0
|
21
|
+
end
|
22
|
+
|
23
|
+
def code_length
|
24
|
+
@generator.length
|
25
|
+
end
|
26
|
+
|
27
|
+
def passcodes_per_line
|
28
|
+
@passcodes_per_line ||= ( (@@CHARS_PER_LINE+1) / (code_length + 1) ).to_i
|
29
|
+
end
|
30
|
+
|
31
|
+
def next_code
|
32
|
+
code = @generator.passcode( @offset )
|
33
|
+
@offset += 1
|
34
|
+
code
|
35
|
+
end
|
36
|
+
|
37
|
+
def passcode row_col
|
38
|
+
raise ArgumentError.new( @@ERROR_BAD_ROW_COL % row_col ) unless row_col.size == 2 && @@ROW_COL_PATTERN.match( row_col.split('').sort.join )
|
39
|
+
passcode *row_col.split('')
|
40
|
+
end
|
41
|
+
|
42
|
+
def passcode row, col
|
43
|
+
col_offset = col.ord - @@FIRST_COLUMN.ord
|
44
|
+
row_offset = row - 1
|
45
|
+
|
46
|
+
@generator.passcode row_offset * passcodes_per_line + col_offset
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'ppp/generator'
|
2
|
+
require 'ppp/card/base'
|
3
|
+
|
4
|
+
class Ppp::Card::Html < Ppp::Card::Base
|
5
|
+
@@SIZES = {:creditcard => %w[ 85.60mm 53.98mm ]}
|
6
|
+
|
7
|
+
def initialize generator, opts={}
|
8
|
+
super
|
9
|
+
|
10
|
+
options = { :font_size => 14, :size => :creditcard }.merge opts
|
11
|
+
@font_size = options[ :font_size ]
|
12
|
+
@size = process_size options[ :size ]
|
13
|
+
end
|
14
|
+
|
15
|
+
def css
|
16
|
+
%[
|
17
|
+
<style>
|
18
|
+
.card, .card .title, .card .card_index, .card .codes, .card .codes td {
|
19
|
+
font-family: monospace;
|
20
|
+
font-size: 3.5mm;
|
21
|
+
}
|
22
|
+
.card {
|
23
|
+
background-color: \#fff;
|
24
|
+
margin: 0 auto 0 auto;
|
25
|
+
width: #{width};
|
26
|
+
height: #{height};
|
27
|
+
border: 1px solid \#000;
|
28
|
+
border-top: none;
|
29
|
+
border-spacing: 0;
|
30
|
+
}
|
31
|
+
.title {
|
32
|
+
border: 1px solid \#000;
|
33
|
+
border-bottom: 1px solid \#000;
|
34
|
+
background-color: \#eee;
|
35
|
+
text-align: left;
|
36
|
+
padding-left: 1ex;
|
37
|
+
font-weight: bold;
|
38
|
+
}
|
39
|
+
.title td:first-child {
|
40
|
+
width: 2.5em;
|
41
|
+
}
|
42
|
+
.card_index {
|
43
|
+
float: right;
|
44
|
+
margin-right: 1ex;
|
45
|
+
}
|
46
|
+
.card_index:before { content: '['; }
|
47
|
+
.card_index:after { content: ']'; }
|
48
|
+
|
49
|
+
.codes_heading th {
|
50
|
+
text-align: center;
|
51
|
+
width: #{code_length}em;
|
52
|
+
border-bottom: 1px solid \#aaa;
|
53
|
+
background-color: \#eee;
|
54
|
+
}
|
55
|
+
.codes_heading th:first-child {
|
56
|
+
background-color: \#fff;
|
57
|
+
border-right: 1px solid \#aaa;
|
58
|
+
}
|
59
|
+
.codes_heading th:last-child { border-right: none; }
|
60
|
+
.codes td {
|
61
|
+
padding: 0;
|
62
|
+
margin: 0;
|
63
|
+
text-align: center;
|
64
|
+
border-right: 1px solid \#aaa;
|
65
|
+
border-bottom: 1px solid \#aaa;
|
66
|
+
}
|
67
|
+
.codes tr td:first-child {
|
68
|
+
text-align: right;
|
69
|
+
padding-right: 0.5em;
|
70
|
+
font-weight: bold;
|
71
|
+
background-color: \#eee;
|
72
|
+
border-right: 1px solid \#aaa;
|
73
|
+
}
|
74
|
+
.codes tr td:first-child { border-bottom: none; }
|
75
|
+
.codes tr td:last-child { border-right: none; }
|
76
|
+
.codes tr:last-child td { border-bottom: none; }
|
77
|
+
</style>
|
78
|
+
]
|
79
|
+
end
|
80
|
+
|
81
|
+
def html
|
82
|
+
%[
|
83
|
+
<table class="card">
|
84
|
+
<caption class="title">
|
85
|
+
#{@title}
|
86
|
+
<span class="card_index">#{@card_index}</span>
|
87
|
+
</caption>
|
88
|
+
<colgroup span="1">
|
89
|
+
<colgroup span="#{passcodes_per_line}">
|
90
|
+
<thead class="codes_heading">
|
91
|
+
<th> </th>
|
92
|
+
#{ (0..passcodes_per_line-1).collect { |i| %[<th>#{(@@FIRST_COLUMN.ord + i).chr}</th>] }.join ?\n }
|
93
|
+
</thead>
|
94
|
+
<tbody class="codes">
|
95
|
+
#{ (1..9).collect { |i| "<tr><td>#{i}</td>#{(1..passcodes_per_line).collect {%[<td>#{next_code}</td>]}.join(?\n) }</tr>" }.join(?\n) }
|
96
|
+
</tbody>
|
97
|
+
</table>
|
98
|
+
]
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_s
|
102
|
+
"#{css}\n\n#{html}"
|
103
|
+
end
|
104
|
+
|
105
|
+
protected # ----------------------------------------------
|
106
|
+
|
107
|
+
def width
|
108
|
+
@size[0]
|
109
|
+
end
|
110
|
+
|
111
|
+
def height
|
112
|
+
@size[1]
|
113
|
+
end
|
114
|
+
|
115
|
+
def process_size size
|
116
|
+
case size
|
117
|
+
when Symbol
|
118
|
+
return @@SIZES[ size ] if @@SIZES.include? size
|
119
|
+
raise ArgumentError.new( %[No card size for symbol "#{size}"] )
|
120
|
+
when Array
|
121
|
+
return [ size[0].to_s, size[1].to_s ]
|
122
|
+
else
|
123
|
+
raise ArgumentError.new( "Size must be a Symbol or Array" )
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'ppp/card/base'
|
2
|
+
|
3
|
+
class Ppp::Card::Plain < Ppp::Card::Base
|
4
|
+
|
5
|
+
def initialize generator, opts={}
|
6
|
+
super
|
7
|
+
|
8
|
+
options = { :row_count => 10 }.merge opts
|
9
|
+
@row_count = options[ :row_count ]
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
line( bar ) +
|
14
|
+
line( :pad, title_str ) +
|
15
|
+
line( split_bar ) +
|
16
|
+
line( :pad, header ) +
|
17
|
+
row_lines.join( '' ) +
|
18
|
+
line( split_bar )
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def bar
|
24
|
+
@bar ||= '-' * (width + 2)
|
25
|
+
end
|
26
|
+
|
27
|
+
def width
|
28
|
+
@width ||= begin
|
29
|
+
return rows.first.join( ' ' ).size if rows.size >= 1
|
30
|
+
header.size
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def rows
|
35
|
+
@rows ||= begin
|
36
|
+
(1..@row_count).collect do |i|
|
37
|
+
[ first_column( i ) ] + (1..passcodes_per_line).collect {next_code}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def header
|
43
|
+
@header ||= header_cells.join ' '
|
44
|
+
end
|
45
|
+
|
46
|
+
def header_cells
|
47
|
+
[' ' * first_column_width ] + (1..passcodes_per_line).collect do |i|
|
48
|
+
(@@FIRST_COLUMN.ord + i-1).chr.center code_length
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def title_str
|
53
|
+
@title_str ||= begin
|
54
|
+
title = @title[0, width - card_label.size] # trim title if it's too long to fit
|
55
|
+
|
56
|
+
blank_space = width - (title.size + card_label.size)
|
57
|
+
title = title + (' ' * blank_space) + card_label
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def card_label
|
62
|
+
@card_label ||= "[#{@card_index}]"
|
63
|
+
end
|
64
|
+
|
65
|
+
def split_bar
|
66
|
+
@split_bar ||= begin
|
67
|
+
parts = ['-' * first_column_width] + (1..passcodes_per_line).collect { '-' * code_length }
|
68
|
+
"-#{parts.join '+'}-"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def row_lines
|
73
|
+
@row_lines ||= rows.collect{ |r| line :pad, r.join( ' ' ) }
|
74
|
+
end
|
75
|
+
|
76
|
+
def line pad=:nopad, str
|
77
|
+
return "| #{str} |\n" if pad == :pad
|
78
|
+
|
79
|
+
"|#{str}|\n"
|
80
|
+
end
|
81
|
+
|
82
|
+
def first_column row_index
|
83
|
+
label = "#{row_index}:"
|
84
|
+
padding = ' ' * (first_column_width - label.size)
|
85
|
+
padding + label
|
86
|
+
end
|
87
|
+
|
88
|
+
def first_column_width
|
89
|
+
@first_column_width ||= @row_count.to_s.size + 1
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'ppp/Cppp'
|
2
|
+
|
3
|
+
# Generates passcodes.
|
4
|
+
class Ppp::Generator
|
5
|
+
attr_reader :seed, :length, :alphabet
|
6
|
+
|
7
|
+
@@HEX_PATTERN = /[a-fA-F0-9]{64}/
|
8
|
+
|
9
|
+
# @param [String] sha256_key a 64 hex-digit string representation of a
|
10
|
+
# SHA-256 hash. This hash will seed all the generated passcodes.
|
11
|
+
# @param [Fixnum] length the number of characters in each generated passcode
|
12
|
+
# @param [String] alphabet a string containing the characters passcodes will
|
13
|
+
# be comprised of. Cannot contain null characters and duplicate characters
|
14
|
+
# will be removed.
|
15
|
+
def initialize sha256_key, opts={}
|
16
|
+
raise NotHexKey.new( sha256_key ) if @@HEX_PATTERN.match( sha256_key ).nil?
|
17
|
+
|
18
|
+
@seed = sha256_key
|
19
|
+
|
20
|
+
options = { :length => 4, :alphabet => :conservative }.merge opts
|
21
|
+
@length = options[ :length ]
|
22
|
+
@alphabet = process_alphabet( options[ :alphabet ] ).split( '' ).uniq.join
|
23
|
+
|
24
|
+
raise ArgumentError.new( "alphabet cannot contain null character" ) if alphabet.include? ?\0
|
25
|
+
end
|
26
|
+
|
27
|
+
# Creates passcodes seeded off the SHA-256 key this object was created with.
|
28
|
+
# Calling this method subsequent times with the same offset will return the
|
29
|
+
# same passcodes, so you should increase the offset by count each time.
|
30
|
+
# @param [Fixnum] offset the number of passcodes to skip
|
31
|
+
# @param [Fixnum] count the number of passcodes to return
|
32
|
+
# @return [Array] an array of passcodes
|
33
|
+
def passcodes offset, count
|
34
|
+
Cppp.passcodes @seed, offset, count, @length, @alphabet
|
35
|
+
end
|
36
|
+
|
37
|
+
# (@see #passcodes)
|
38
|
+
def passcode offset
|
39
|
+
passcodes( offset, 1 ).first
|
40
|
+
end
|
41
|
+
|
42
|
+
# Check if a given passcode is correct
|
43
|
+
# @param [Fixnum] index the index of the passcode to check against
|
44
|
+
# @return [Boolean] if the given passcode matches the passcode at the given offset
|
45
|
+
def verify index, given_passcode
|
46
|
+
passcode( index ) == given_passcode
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def process_alphabet alphabet
|
52
|
+
case alphabet
|
53
|
+
when Symbol
|
54
|
+
return Ppp.default_alphabets[ alphabet ] if Ppp.default_alphabets.include? alphabet
|
55
|
+
raise ArgumentError.new( %[No alphabet for for symbol "#{alphabet}"] )
|
56
|
+
when String
|
57
|
+
return alphabet
|
58
|
+
else
|
59
|
+
raise ArgumentError.new( "Alphabet must be a Symbol or String" )
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class NotHexKey < ArgumentError
|
64
|
+
@@error = 'Expected a 64 digit hex-string, but got "%s". Use Ppp.key_from_string() to generate a useable hex-string from an arbitrary string.'
|
65
|
+
def initialize( key ) @key = key end
|
66
|
+
|
67
|
+
def to_s() @@error % @key end
|
68
|
+
end
|
69
|
+
end
|