Skip to content

Commit 94e1d2f

Browse files
committed
New feature: secret sharing for CA passphrase, based on
OpenXPKI::Crypto::Secret::Split (Shamir's Secret Sharing). Uses a nasty hack to simulate the OpenXPKI class hierarchy, but works for now. Includes two runbooks for a simple CA and one with Secret Sharing.
1 parent 705f72b commit 94e1d2f

File tree

11 files changed

+1297
-0
lines changed

11 files changed

+1297
-0
lines changed

README.keyceremony-shared-interactive

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
This is an example runbook for an interactive key ceremony using Secret Sharing.
2+
3+
2013-12-16 Martin Bartosch
4+
5+
6+
Assumptions:
7+
2048 Bit RSA key protected by a 128 Bit random pass phrase.
8+
The pass phrase is split into 5 shares, of which 3 will be needed to perform CA operations.
9+
10+
11+
1. Preparation of CLCA configuration
12+
13+
export K=3
14+
export N=5
15+
rm -rf dummyca/
16+
mkdir -p dummyca/etc
17+
mkdir -p dummyca/private/
18+
chmod 700 dummyca/private/
19+
cp etc/clca.cfg dummyca/etc/
20+
cp etc/openssl.cnf dummyca/etc/
21+
22+
cat <<EOF >>dummyca/etc/clca.cfg
23+
get_passphrase() {
24+
../bin/secret get --n $N --k $K
25+
}
26+
EOF
27+
28+
29+
2. Generate CA key and perform secret sharing.
30+
31+
Required: you need N=5 persons for safekeeping of the CA shares.
32+
33+
PASSPHRASE=`./bin/secret generate --n $N --k $K` openssl genrsa -aes256 -passout env:PASSPHRASE -out dummyca/private/rsa-rootkey 2048
34+
35+
Each share holder must copy the displayed share literally and keep it.
36+
37+
38+
3. Create the CA certificate
39+
40+
cd dummyca
41+
../bin/clca initialize
42+
43+
4. Create initial CRL
44+
45+
../bin/clca issue_crl
46+
47+
5. Sign certificate
48+
49+
../bin/clca certify REQUEST
50+
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
This is an example runbook for a noninteractive key ceremony using plain/static keys.
2+
3+
2013-12-16 Martin Bartosch
4+
5+
6+
7+
Assumptions:
8+
2048 Bit RSA key protected by the passphrase '1234'.
9+
10+
11+
1. Preparation of CLCA configuration
12+
13+
rm -rf dummyca/
14+
mkdir -p dummyca/etc
15+
mkdir -p dummyca/private/
16+
chmod 700 dummyca/private/
17+
cp etc/clca.cfg dummyca/etc/
18+
cp etc/openssl.cnf dummyca/etc/
19+
20+
cat <<EOF >>dummyca/etc/clca.cfg
21+
get_passphrase() {
22+
echo "1234"
23+
}
24+
EOF
25+
26+
27+
2. Generate CA key and perform secret sharing.
28+
29+
PASSPHRASE="1234" openssl genrsa -aes256 -passout env:PASSPHRASE -out dummyca/private/rsa-rootkey 2048
30+
31+
32+
3. Create the CA certificate
33+
34+
cd dummyca
35+
../bin/clca initialize
36+
37+
4. Create initial CRL
38+
39+
../bin/clca issue_crl
40+
41+
5. Sign certificate
42+
43+
../bin/clca certify REQUEST
44+

bin/secret

+250
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
#!/usr/bin/env perl
2+
#
3+
# Command line tool wrapper for OpenXPKI Secret Sharing
4+
# 2013-12-04 Martin Bartosch <[email protected]>
5+
#
6+
7+
# Stub implementation for OpenXPKI Crypto Token (only random numbers and prime tests required)
8+
package DummyToken;
9+
use strict;
10+
use warnings;
11+
12+
sub new {
13+
my $self = {};
14+
bless $self;
15+
16+
return $self;
17+
}
18+
19+
sub command {
20+
my $self = shift;
21+
my $arg = shift;
22+
23+
if ($arg->{COMMAND} eq 'create_random') {
24+
my $len = $arg->{RANDOM_LENGTH};
25+
my $rand = `openssl rand -base64 $len`;
26+
if ($? != 0) {
27+
die "ERROR: could not run openssl";
28+
}
29+
chomp $rand;
30+
if (length($rand) < $len) {
31+
die "Error: could not create random value. Stopped";
32+
}
33+
return $rand;
34+
}
35+
36+
if ($arg->{COMMAND} eq 'is_prime') {
37+
my $result = `openssl prime -hex $arg->{PRIME}`;
38+
chomp $result;
39+
if ($? != 0) {
40+
die "ERROR: could not run openssl";
41+
}
42+
if ($result =~ m{ is\ not\ prime \z}xms) {
43+
return;
44+
} elsif ($result =~ m{ is\ prime \z}xms) {
45+
return 1;
46+
} else {
47+
die "ERROR: invalid prime test result $result returned from openssl";
48+
}
49+
}
50+
die "token command " . $arg->{COMMAND} . " not implemented";
51+
}
52+
53+
54+
55+
package main;
56+
57+
use strict;
58+
use warnings;
59+
60+
use FindBin;
61+
use lib "$FindBin::Bin/../lib";
62+
63+
use Getopt::Long;
64+
use Pod::Usage;
65+
use OpenXPKI::Crypto::Secret;
66+
use Data::Dumper;
67+
68+
use English;
69+
70+
my $k = 2;
71+
my $n = 3;
72+
my $bitlength = 128;
73+
my $help;
74+
my $man;
75+
my $batch;
76+
77+
GetOptions(
78+
'help' => \$help,
79+
'man' => \$man,
80+
'k=i' => \$k,
81+
'n=i' => \$n,
82+
'bitlength=i' => \$bitlength,
83+
'batch' => \$batch,
84+
)
85+
or pod2usage(-verbose => 0);
86+
87+
pod2usage(-exitstatus => 0, -verbose => 2) if ($man);
88+
pod2usage(-verbose => 1) if ($help);
89+
90+
91+
my $secret = OpenXPKI::Crypto::Secret->new(
92+
{
93+
TYPE => 'Split',
94+
QUORUM => {
95+
N => $n,
96+
K => $k,
97+
},
98+
TOKEN => DummyToken->new(),
99+
});
100+
101+
if (! $secret) {
102+
die "Could not instantiate secret. Stopped";
103+
}
104+
105+
my $cmd = shift || exit 0;
106+
107+
if ($cmd eq 'generate') {
108+
my @shares = $secret->compute({ BITLENGTH => $bitlength });
109+
my $secret = $secret->get_secret();
110+
111+
if ($batch) {
112+
print "K=$k\n";
113+
print "N=$n\n";
114+
print "PASSPHRASE=$secret\n";
115+
116+
for (my $ii = 0; $ii < scalar @shares; $ii++) {
117+
print "SHARE[$ii]=$shares[$ii]\n";
118+
}
119+
} else {
120+
print STDERR "Creating shared secret of $bitlength random bits with ($k/$n) quorum.\n\n";
121+
print STDERR "This program will first print the $n shares to STDERR, clearing the screen\n";
122+
print STDERR "with 200 newlines after each step.\n";
123+
print STDERR "After providing the shares the program will print the $bitlength bit phrase\n";
124+
print STDERR "to STDOUT.\n\n";
125+
126+
print STDERR "Hit ENTER now to display the first share.\n";
127+
my $tmp = <>;
128+
for (my $ii = 0; $ii < $n; $ii++) {
129+
print STDERR "Share holder #" . ($ii + 1) . ", this is your share:\n";
130+
print STDERR $shares[$ii] . "\n\n";
131+
print STDERR "Copy the share and hit ENTER to clear the screen.\n";
132+
print STDERR "(NOTE: no sensitive data will be displayed immediately after the key press.)\n";
133+
$tmp = <>;
134+
for (my $jj = 0; $jj < 200; $jj++) {
135+
print STDERR "\n";
136+
}
137+
print STDERR "Share #" . ($ii + 1) . " has just been displayed.\n";
138+
print STDERR "Hit ENTER to continue\n\n";
139+
$tmp = <>;
140+
}
141+
print "PASSPHRASE=$secret\n";
142+
}
143+
144+
} elsif ($cmd eq 'get') {
145+
while (! $secret->is_complete()) {
146+
if (! $batch) {
147+
for (my $ii = 0; $ii < 200; $ii++) {
148+
print STDERR "\n";
149+
}
150+
print STDERR "Please enter share: ";
151+
}
152+
my $line = <>;
153+
chomp $line;
154+
$line = uc($line);
155+
eval {
156+
$secret->set_secret($line);
157+
};
158+
if ($EVAL_ERROR) {
159+
print STDERR "ERROR: invalid share entered, repeat please\n";
160+
if (! $batch) {
161+
sleep 2;
162+
}
163+
}
164+
}
165+
print "PASSPHRASE=" . $secret->get_secret() . "\n";
166+
167+
} else {
168+
print STDERR "ERROR: invalid command '$cmd'\n";
169+
exit 1;
170+
}
171+
172+
__END__
173+
174+
=head1 NAME
175+
176+
secret - Secret Splitting
177+
178+
=head1 USAGE
179+
180+
secret [OPTIONS] [get|generate]
181+
182+
=head1 DESCRIPTION
183+
184+
=head1 OPTIONS
185+
186+
=over 8
187+
188+
=item B<--k>
189+
190+
Quorum value K. Default: 2
191+
K shares out of N are required to reconstruct the secret.
192+
193+
=item B<--n>
194+
195+
Quorum value N. Default: 3
196+
K shares out of N are required to reconstruct the secret.
197+
198+
=item B<--batch>
199+
200+
Batch mode, does not print instructions for humans, instead only outputs the necessary
201+
information.
202+
203+
=item B<--help>
204+
205+
Display a short help summary.
206+
207+
=item B<--man>
208+
209+
Display full manual page.
210+
211+
=back
212+
213+
=head1 COMMANDS
214+
215+
=over 8
216+
217+
=item B<generate>
218+
219+
This command generates a random number (default: 128 bit), performs the actual secret
220+
splitting, generates and prints the secret parts. The output in batch mode has the format
221+
222+
SECRET=....
223+
SHARE[0]=....
224+
SHARE[1]=....
225+
226+
A useful way to hide the output and to provide the data for later processing (e. g. during a key
227+
ceremony) is to invoke the script via eval `...`:
228+
229+
eval `./bin/secret generate --k 3 --n 5 --batch`
230+
231+
After executing ths command you can access the values as shell variables.
232+
233+
In interactive mode (default) some verbose instructions are printed to STDERR, the final SECRET=...
234+
is still printed to STOUT. This makes it possible to assign the secret value to a variable and
235+
continue processing from there:
236+
237+
PASSPHRASE=`./bin/secret generate --k 3 --n 5` openssl genrsa -aes256 -passout env:PASSPHRASE -out key.pem 2048
238+
239+
240+
=item B<get>
241+
242+
After reading the necessary components of the secret this command outputs the reconstructed secret to STDOUT.
243+
Make sure to specify the same --k and --n parameters as during generation of shared secrets.
244+
245+
In --batch mode the script reads --k lines from STDIN without printing any information.
246+
247+
=back
248+
249+
=head1 EXAMPLES
250+

0 commit comments

Comments
 (0)