ppp 0.1.2.alpha
Sign up to get free protection for your applications and to get access to all the features.
- 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
|