Commit 4403805f authored by marcus-tun's avatar marcus-tun
Browse files

Merge branch 'master' of git.scc.kit.edu:lo0018/pluto

Merging in daniels work
parents 6c565864 a5f6624b
*.pyc *.py[cod]
*.swp *.swp
*.log
.gitignore .gitignore
.env
...@@ -15,6 +15,11 @@ Dependencies: ...@@ -15,6 +15,11 @@ Dependencies:
libcrypt-generatepassword-perl libcrypt-generatepassword-perl
libcrypt-cbc-perl libcrypt-cbc-perl
- Interoperability, local environment, e.g. for python:
virtualenv .env
source .env/bin/activate
pip install -r interop/py/requirements.txt
Installation of the Server Installation of the Server
========================== ==========================
Pluto: Pluto:
......
...@@ -287,34 +287,36 @@ my $assertion=$response_content; ...@@ -287,34 +287,36 @@ my $assertion=$response_content;
# ENCRYPTION # # ENCRYPTION #
################ ################
my $restrict='!@#$%^&*()_-=+/' ; my $restrict='!@#$%^&*()_-=+/' ;
my $symmetric_key =chars(5,32,0,$restrict,0); my $symmetric_passwd =chars(5,32,0,$restrict,0);
my $iv='12333123'; my $iv='';
print "key: ".$symmetric_key."\n";
# encryption # encryption
#my $encryption_algorithm='Twofish'; #my $encryption_algorithm='Twofish';
my $encryption_algorithm='Blowfish'; #my $encryption_algorithm='Blowfish';
my $cipher = new Crypt::CBC ($symmetric_key, $encryption_algorithm, -iv=>$iv, -header=>'none'); my $encryption_algorithm='Rijndael';
my $encryption_algorithm_name='AES'; # only for the post, so the server knows what to use
print ("length(assertion):". length($assertion)."\n"); my $cipher = new Crypt::CBC ($symmetric_passwd, $encryption_algorithm, -iv=>$iv, -header=>'none');
my $encrypted_assertion= $cipher->encrypt($assertion); my $encrypted_assertion_tmp= $cipher->encrypt($assertion);
# Remove the first 16 bytes => TODO REMOVE THIS!!!!!
my $encrypted_assertion=substr($encrypted_assertion_tmp, 16);
undef $assertion; undef $assertion;
# extract actual password and initialisation vector # extract actual password and initialisation vector
my $perl_pass = unpack('H*', $cipher->key()); my $perl_key = unpack('H*', $cipher->key());
#my $perl_key = $cipher->key();
my $perl_iv = $cipher->iv(); my $perl_iv = $cipher->iv();
## log to file ## log to file
#open (FILE, ">", 'log.perl'); #open (FILE, ">", 'log.perl');
#print FILE "pass: ".$perl_pass."\n"; #print FILE "pass: ".$perl_key."\n";
#print FILE "iv: ".$perl_iv."\n"; #print FILE "iv: ".$perl_iv."\n";
#print FILE $encrypted_assertion."\n"; #print FILE $encrypted_assertion."\n";
#close (FILE); #close (FILE);
## decrypt ## decrypt
#my $de_cipher = new Crypt::CBC ($symmetric_key, $encryption_algorithm, -iv=>'asdlsdff', -header=>'asdf'); #my $de_cipher = new Crypt::CBC ($symmetric_passwd, $encryption_algorithm, -iv=>'asdlsdff', -header=>'asdf');
##$cipher = new Crypt::ECB ($symmetric_key, $encryption_algorithm); #$de_cipher->iv('asdfasdf');
##$cipher = new Crypt::ECB ($symmetric_passwd, $encryption_algorithm);
#my $plaintext = $de_cipher->decrypt($encrypted_assertion); #my $plaintext = $de_cipher->decrypt($encrypted_assertion);
#print ("\n".$plaintext."\n"); #print ("\n".$plaintext."\n");
...@@ -325,9 +327,9 @@ undef $encrypted_assertion; ...@@ -325,9 +327,9 @@ undef $encrypted_assertion;
# upload encrypted assertion # upload encrypted assertion
$response=$ua->post(CREDENTIAL_UPLOAD_URL, $response=$ua->post(CREDENTIAL_UPLOAD_URL,
[encrypted_assertion =>$b64_encrypted_assertion, [encrypted_assertion =>$b64_encrypted_assertion,
#key => $perl_pass, key => $perl_key,
perl_iv => $perl_iv, iv => $perl_iv,
crypto_algo => $encryption_algorithm, encryption_algorithm => $encryption_algorithm_name,
client => "perl", client => "perl",
client_verion => VERSION]); client_verion => VERSION]);
...@@ -340,7 +342,7 @@ print Dumper($response_content); ...@@ -340,7 +342,7 @@ print Dumper($response_content);
# Store the returned URL # Store the returned URL
(undef, $url_to_assertion) = split("url=", $response_content); (undef, $url_to_assertion) = split("url=", $response_content);
my $url_to_assertion_with_key = $url_to_assertion."&k=".$symmetric_key; my $url_to_assertion_with_key = $url_to_assertion."&k=".$symmetric_passwd;
open (FILE, ">", $urlfile) or die $!; open (FILE, ">", $urlfile) or die $!;
print FILE $url_to_assertion_with_key."\n"; print FILE $url_to_assertion_with_key."\n";
......
#!/usr/bin/env bash
# notes:
# - ./interop.sh "Hello"
# - beware: the base64 encoded cipher does _not_ have the iv prepended
set -e
PASS=$(openssl rand -hex 16)
echo "$@" | openssl enc -aes-128-cbc -nosalt -a -k $PASS -p
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# notes:
# - local server, to test JS GET/POST (b/c of same origin policy), uses Flask, see: http://flask.pocoo.org/
# - deps: pip install -r requirements.txt
# - actual html+js is in 'templates' directory
from __future__ import print_function, division, with_statement # welcome to the future
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('interop.html')
@app.route('/assertion')
def assertion():
app.logger.info('sending: Hello')
return 'Hello'
@app.route('/upload', methods=['POST'])
def upload():
app.logger.info('received: {0}'.format(request.form))
return 'received'
def main():
app.run(host='127.0.0.1', port=8080, debug=True)
if __name__ == '__main__':
main()
# vim: set tabstop=4 shiftwidth=4 expandtab:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>AES (in CBC mode) language interop tester</title>
<!-- TODO(daniel): host locally; jquery v1 for included IE support.. --!>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
</head>
<body>
<h3>AES-128 CBC, PKCS#7, nosalt</h3>
<hr>
<div>
<input id="plaintext" type="text" name="plaintext" autofocus="autofocus" placeholder="My Secret..">
<p id="ciphertext"></p>
<p id="key"></p>
</div>
<h3>Assertion Handling</h3>
<hr>
<div>
<button id="get-btn">Perform GET Request</button>
<p id="get-rv">..</p>
<button id="post-btn">Perform POST Request</button>
<p id="post-rv">..</p>
</div>
</body>
<script>
/* notes:
- this script block does not (yet?) use and therefore depend on jQuery for modularity reasons
- TODO: refactor this poc into a encrypt() function; remove console debugging
*/
"use strict";
var bs = 16; // aes block size is 16 bytes
document.getElementById('plaintext').onkeypress = function(e) {
var event = e || window.event;
var charCode = event.which || event.keyCode;
if (charCode == '13') { // enter pressed
var plaintext = document.getElementById("plaintext").value;
// note: no need for a key derivation function, we're _generating_ a key from scratch _every time_
var key = CryptoJS.lib.WordArray.random(bs);
var iv = CryptoJS.lib.WordArray.random(bs);
var opts = {iv: iv, keySize: bs, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7};
var enc = CryptoJS.AES.encrypt(plaintext, key, opts);
console.log('ciphertext: ' + enc.ciphertext.toString(CryptoJS.enc.Hex));
console.log('key: ' + enc.key.toString(CryptoJS.enc.Hex));
console.log('iv: ' + enc.iv.toString(CryptoJS.enc.Hex));
/* note:
- no need for urlencode, hex is perfectly fine for urls
- decoding: unhex 'out', first 16 bytes are iv, rest is ciphertext
- or: split 'out', unhex first 32 bytes for iv, unhex rest for ciphertext
*/
var out = enc.iv.toString(CryptoJS.enc.Hex) + enc.ciphertext.toString(CryptoJS.enc.Hex);
document.getElementById("ciphertext").textContent = 'iv + ciphertext: ' + out;
document.getElementById("key").textContent = 'key: ' + enc.key.toString(CryptoJS.enc.Hex);
}
}
</script>
<script>
/* notes:
- full blown jQuery solution to keep my sanity; GET/POST assertion poc
- use local server for testing, same origin policy forbids requests otherwise
- ./runserver.py (and check source)
- TODO: chain post(encrypt(get()))
*/
"use strict";
var from = "http://127.0.0.1:8080/assertion";
var to = "http://127.0.0.1:8080/upload";
/*
var from = "https://saml-delegation.data.kit.edu/sd/ecp.py";
var to = "https://saml-delegation.data.kit.edu/sd/upload.py";
*/
$(document).ready(function() {
$("#get-btn").click(function() {
$("#get-rv").text(".."); // clear old state
$.get(from, function(data, stat){
$("#get-rv").text(stat);
});
});
$("#post-btn").click(function() {
var assertion = {role: 'dummy'}; // XXX: aes-cbc-128-pkcs#7-nosalt(get(assertion))
$("#post-rv").text(".."); // clear old state
$.post(to, assertion, function(data, stat){
$("#post-rv").text(stat);
});
});
});
</script>
</html>
<!-- vim: set tabstop=4 shiftwidth=4 expandtab: --!>
#!/usr/bin/env perl
use strict;
use Getopt::Long qw(:config bundling);
use Crypt::CBC;
use MIME::Base64 qw( encode_base64 );
use MIME::Base64 qw( decode_base64 );
my %opts = ();
my $verbose=0;
my $iv='0000000000000000';
my $pass='0000000000000000';
my $plaintext='hello world, today is a good day to have long strings';
my $salt='00000000'; # 0: off -- 1: on -- >1: actual salt
my $unhex=0;
# Get options
GetOptions(\%opts, 'help|h',
'verbose|v',
'iv|i=s',
'pass|p=s',
'plaintext|t=s',
'ciphertext|c=s',
'salt|s=s',
'unhex|u',
) or exit;
if (exists $opts{help}) {
print ("Usage: interop.pl [arguments]
-v, --verbose
-i, --iv
-k, --pass
-t, --plaintext
-c, --ciphertext
-s, --salt
\n");
exit (0);
}
if (exists $opts{verbose}) {
$verbose = 1;
}
if (exists $opts{iv}) {
$iv = trim($opts{iv});
}
if (exists $opts{pass}) {
$pass = trim($opts{pass});
}
if (exists $opts{plaintext}) {
$plaintext = trim($opts{plaintext});
}
if (exists $opts{salt}) {
$salt = trim($opts{salt});
}
## Debug output
print ("plaintext: ".$plaintext."\n") if $verbose;
print ("iv: ".$iv."\n") if $verbose;
print ("pass: ".$pass."\n") if $verbose;
print ("salt: ".$salt."\n") if $verbose;
## ENCRYPTION
my $encryption_algorithm='Rijndael';
#my $cipher = new Crypt::CBC ($pass, $encryption_algorithm, -literal_key=>1, -iv=>$iv, -header=>'none', -salt=>$salt -keysize=>16, -blocksize=>16);
my $cipher = new Crypt::CBC ($pass, $encryption_algorithm, -iv=>$iv, -header=>'none', -salt=>$salt);
my $ciphertext= encode_base64($cipher->encrypt($plaintext));
my $perl_iv = $cipher->iv();
#my $perl_key = unpack('H*', $cipher->key());
my $perl_key = $cipher->key();
print ('perl_iv: '.$perl_iv."\n") if $verbose;
## DECRYPTION
if (exists $opts{ciphertext}) {
$ciphertext = trim($opts{ciphertext});
print ("Using ciphertext from cmdline\n");
}
print ("ciphertext: ".$ciphertext."\n") if $verbose;
print ("ciphertext_de64: ".decode_base64($ciphertext)."\n") if $verbose;
my $de_cipher = new Crypt::CBC ($pass, $encryption_algorithm, -iv=>$iv, -header=>'none'-salt=>$salt);
my $new_plaintext = $de_cipher->decrypt(decode_base64($ciphertext));
print ("\n");
print ("decrypted: ".$new_plaintext."\n") if $verbose;
## Check if things worked
print ("worked \n") if ($plaintext eq $new_plaintext);
#########################################################################
# subroutine: trim($str) #
# parameter : $str - a string to trim spaces from. #
# returns : the passed-in string with leading and trailing spaces #
# removed. #
# this subroutine removes leading and trailing spaces from the #
# passed-in string. note that the original string is not modified. #
# rather, a new string without leading/trailing spaces is returned. #
#########################################################################
sub trim
{
my $str = shift;
$str =~ s/^\s+//;
$str =~ s/\s+$//;
return $str;
}
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# notes:
# - deps: pip install -r requirements.txt
# - keep it clean: flake8
from __future__ import print_function, division, with_statement # welcome to the future
import argparse
import logging
import base64
from Crypto.Cipher import AES
def _config():
parser = argparse.ArgumentParser(description='AES (in CBC mode) language interop tester')
parser.add_argument('-i', '--iv', dest='iv', default='{0}'.format(AES.block_size * '0'),
help='initialization vector, has to be of size {0} bytes'.format(AES.block_size))
parser.add_argument('-k', '--key', dest='key', default='{0}'.format(AES.key_size[0] * '0'),
help='key, size has to be {0} bytes'.format(" or ".join(map(lambda i: str(i), AES.key_size))))
parser.add_argument('-v', '--verbose', dest='verbose', action='store_true',
help='increased output verbosity')
parser.add_argument('-p', '--plaintext', dest='plaintext', required=True,
help='plaintext to encrypt')
args = parser.parse_args()
if args.verbose:
logging.basicConfig(level=logging.INFO, format='%(funcName)s: %(message)s')
rv = {'iv': args.iv, 'key': args.key, 'in': args.plaintext}
logging.info(rv)
return rv
# returned cipher object is stateful
def _aes_cipher(key, iv):
logging.info({'key': key, 'iv': iv})
assert(len(iv) == AES.block_size), 'iv size mismatch'
assert(len(key) in AES.key_size), 'key size mismatch'
return AES.new(key, AES.MODE_CBC, iv)
# PKCS#7 padding scheme, see:
# - 10.3/2 http://tools.ietf.org/html/rfc2315
# - http://stackoverflow.com/a/14205319
def _pkcs7(bs, x):
rv = x + (bs - len(x) % bs) * chr(bs - len(x) % bs)
logging.info({'blocksize': bs, 'raw': x, 'padded': rv})
assert(len(rv) % AES.block_size == 0), 'padded plaintext is not a multiple of block size'
return rv
def _unpkcs7(x):
rv = x[0:-ord(x[-1])]
logging.info({'raw': x, 'unpadded': rv})
assert(_pkcs7(AES.block_size, rv) == x), 'unpadding does not reverse padding'
return rv
# iv + aes(in + pkcs7_pad)
def encrypt(key, iv, plaintext):
aes = _aes_cipher(key, iv)
padded = _pkcs7(AES.block_size, plaintext)
return iv + aes.encrypt(padded)
# first AES.block_size is iv, rest is aes(in + pad)
def decrypt(key, ciphertext):
iv, padded = ciphertext[:AES.block_size], ciphertext[AES.block_size:]
aes = _aes_cipher(key, iv)
return _unpkcs7(aes.decrypt(padded))
# external (read: from the internet) test vector
def _selftest_nosalt():
key = '603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4'.decode('hex')
iv = '000102030405060708090A0B0C0D0E0F'.decode('hex')
plaintext = '6bc1bee22e409f96e93d7e117393172a'.decode('hex')
ciphertext = 'f58c4c04d6e5f1ba779eabfb5f7bfbd6485a5c81519cf378fa36d42b8547edc0'.decode('hex')
expected = iv + ciphertext
enc = encrypt(key, iv, plaintext)
logging.info({'expected': ciphertext, 'encrypted': enc})
assert(enc == expected), 'encryption does not match expected result'
dec = decrypt(key, enc)
logging.info({'expected': plaintext, 'decrypted': dec})
assert(dec == plaintext), 'encryption decryption round robin mismatch'
# XXX: openssl's 'Salted__' + salt prepended scheme, not implemented yet
# see: http://security.stackexchange.com/a/23239 for what a salt does to iv, key
def _selftest_salt():
pass
def main():
cfg = _config()
_selftest_nosalt()
_selftest_salt()
enc = encrypt(cfg['key'], cfg['iv'], cfg['in'])
b64 = base64.b64encode(enc)
print(b64)
raw = base64.b64decode(b64)
dec = decrypt(cfg['key'], raw)
assert(cfg['in'] == dec), 'encryption decryption round robin mismatch'
if __name__ == '__main__':
main()
# vim: set tabstop=4 shiftwidth=4 expandtab:
flake8==2.2.4
pycrypto==2.6.1
[flake8]
filename = *.py,*.cfg
max-line-length = 120
show-source = True
show-pep8 = False
max-complexity = 10
exclude = ./env/*,./setup.cfg
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment