--- AnyEvent-MP/MP/Transport.pm 2009/08/03 22:05:55 1.13 +++ AnyEvent-MP/MP/Transport.pm 2009/08/04 23:35:51 1.21 @@ -29,6 +29,9 @@ use Storable (); use JSON::XS (); +use Digest::MD6 (); +use Digest::HMAC_MD6 (); + use AE (); use AnyEvent::Socket (); use AnyEvent::Handle (); @@ -128,19 +131,24 @@ { Scalar::Util::weaken (my $self = $self); - $arg{tls_ctx_disabled} ||= { - sslv2 => 0, - sslv3 => 0, - tlsv1 => 1, - verify => 1, - cert_file => "secret.pem", - ca_file => "secret.pem", - verify_require_client_cert => 1, - }; - $arg{secret} = AnyEvent::MP::Base::default_secret () unless exists $arg{secret}; + my $secret = $arg{secret}; + + if ($secret =~ /-----BEGIN RSA PRIVATE KEY-----.*-----END RSA PRIVATE KEY-----.*-----BEGIN CERTIFICATE-----.*-----END CERTIFICATE-----/s) { + # assume TLS mode + $arg{tls_ctx} = { + sslv2 => 0, + sslv3 => 0, + tlsv1 => 1, + verify => 1, + cert => $secret, + ca_cert => $secret, + verify_require_client_cert => 1, + }; + } + $self->{hdl} = new AnyEvent::Handle fh => delete $arg{fh}, autocork => 1, @@ -151,7 +159,6 @@ peername => delete $arg{peername}, ; - my $secret = $arg{secret}; my $greeting_kv = $self->{greeting} ||= {}; $greeting_kv->{"tls"} = "1.0" if $arg{tls_ctx}; @@ -219,34 +226,36 @@ $self->{hdl}->push_read (line => sub { my $rgreeting2 = $_[1]; + "$lgreeting1\012$lgreeting2" ne "$rgreeting1\012$rgreeting2" # echo attack? + or return $self->error ("authentication error, echo attack?"); + + my $key = Digest::MD6::md6 $secret; + my $lauth; + if ($self->{tls_ctx} and 1 == int $self->{remote_greeting}{tls}) { $self->{tls} = $lgreeting2 lt $rgreeting2 ? "connect" : "accept"; $self->{hdl}->starttls ($self->{tls}, $self->{tls_ctx}); + $s_auth = "tls"; + $lauth = ""; + } else { + # we currently only support hmac_md6_64_256 + $lauth = Digest::HMAC_MD6::hmac_md6_hex $key, "$lgreeting1\012$lgreeting2\012$rgreeting1\012$rgreeting2\012", 64, 256; } - - # auth - require Digest::MD6; - require Digest::HMAC_MD6; - - my $key = Digest::MD6::md6 ($secret); - my $lauth = Digest::HMAC_MD6::hmac_md6_hex ($key, "$lgreeting1\012$lgreeting2\012$rgreeting1\012$rgreeting2\012", 64, 256); - - my $rauth = - $s_auth eq "hmac_md6_64_256" ? Digest::HMAC_MD6::hmac_md6_hex ($key, "$rgreeting1\012$rgreeting2\012$lgreeting1\012$lgreeting2\012", 64, 256) - : $s_auth eq "cleartext" ? unpack "H*", $secret - : die; - - $lauth ne $rauth # echo attack? - or return $self->error ("authentication error"); $self->{hdl}->push_write ("$s_auth;$lauth;$s_framing\012"); - # reasd the authentication response + # read the authentication response $self->{hdl}->push_read (line => sub { my ($hdl, $rline) = @_; my ($auth_method, $rauth2, $r_framing) = split /;/, $rline; + my $rauth = + $auth_method eq "hmac_md6_64_256" ? Digest::HMAC_MD6::hmac_md6_hex $key, "$rgreeting1\012$rgreeting2\012$lgreeting1\012$lgreeting2\012", 64, 256 + : $auth_method eq "cleartext" ? unpack "H*", $secret + : $auth_method eq "tls" ? ($self->{tls} ? "" : "\012\012") # \012\012 never matches + : return $self->error ("$auth_method: fatal, selected unsupported auth method"); + if ($rauth2 ne $rauth) { return $self->error ("authentication failure/shared secret mismatch"); } @@ -282,6 +291,8 @@ my ($self, $msg) = @_; if ($self->{node} && $self->{node}{transport} == $self) { + #TODO: store error, but do not instantly fail + $self->{node}->fail (transport_error => $self->{node}{noderef}, $msg); $self->{node}->clr_transport; } $AnyEvent::MP::Base::WARN->("$self->{peerhost}:$self->{peerport}: $msg"); @@ -328,18 +339,26 @@ =head2 GREETING +All the lines until after authentication must not exceed 4kb in length, +including delimiter. Afterwards there is no limit on the packet size that +can be received. + +=head3 First Greeting Line + +Example: + + aemp;0;fec.4a7720fc;127.0.0.1:1235,[::1]:1235;hmac_md6_64_256;json,storable;provider=AE-0.0 + The first line contains strings separated (not ended) by C<;> characters. The first even ixtrings are fixed by the protocol, the remaining strings are C pairs. None of them may contain C<;> characters themselves. -All the lines until after authentication must not exceed 4kb in length, including delimiter. - The fixed strings are: =over 4 -=item C +=item protocol identification The constant C to identify the protocol. @@ -347,7 +366,7 @@ The protocol version supported by this end, currently C<0>. If the versions don't match then no communication is possible. Minor extensions -are supposed to be handled by addign additional key-value pairs. +are supposed to be handled through additional key-value pairs. =item a token uniquely identifying the current node instance @@ -399,21 +418,25 @@ =back +=head3 Second Greeting Line + After this greeting line there will be a second line containing a cryptographic nonce, i.e. random data of high quality. To keep the protocol text-only, these are usually 32 base64-encoded octets, but it could be anything that doesn't contain any ASCII CR or ASCII LF characters. -Example of the two lines of greeting: +I<< The two nonces B be different, and an aemp implementation +B check and fail when they are identical >>. + +Example of a nonce line: - aemp;0;fec.4a7720fc;127.0.0.1:1235,[::1]:1235;hmac_md6_64_256;json,storable;provider=AE-0.0 p/I122ql7kJR8lumW3lXlXCeBnyDAvz8NQo3x5IFowE4 =head2 TLS handshake -If, after the handshake, both sides indicate interest in TLS, then the -connection I use TLS, or fail. +I<< If, after the handshake, both sides indicate interest in TLS, then the +connection B use TLS, or fail. >> Both sides compare their nonces, and the side who sent the lower nonce value ("string" comparison on the raw octet values) becomes the client, @@ -468,6 +491,14 @@ This is the token that is expected from the other side. +=item tls + +This type is only valid iff TLS was enabled and the TLS handshake +was successful. It has no authentication data, as the server/client +certificate was successfully verified. + +Implementations supporting TLS I accept this authentication type. + =back =item the authentication data @@ -482,7 +513,7 @@ =back -Example: +Example of an authentication reply: hmac_md6_64_256;363d5175df38bd9eaddd3f6ca18aa1c0c4aa22f0da245ac638d048398c26b8d3;json @@ -491,6 +522,20 @@ After this, packets get exchanged using the chosen framing protocol. It is quite possible that both sides use a different framing protocol. +=head2 FULL EXAMPLE + +This is an actual protocol dump of a handshake, followed by a single data +packet. The greater than/less than lines indicate the direction of the +transfer only. + + > aemp;0;nndKd+gn;10.0.0.1:4040;hmac_md6_64_256,cleartext;json,storable;provider=AE-0.0;peeraddr=127.0.0.1:1235 + > sRG8bbc4TDbkpvH8FTP4HBs87OhepH6VuApoZqXXskuG + < aemp;0;nmpKd+gh;127.0.0.1:1235,[::1]:1235;hmac_md6_64_256,cleartext;json,storable;provider=AE-0.0;peeraddr=127.0.0.1:58760 + < dCEUcL/LJVSTJcx8byEsOzrwhzJYOq+L3YcopA5T6EAo + > hmac_md6_64_256;9513d4b258975accfcb2ab7532b83690e9c119a502c612203332a591c7237788;json + < hmac_md6_64_256;0298d6ba2240faabb2b2e881cf86b97d70a113ca74a87dc006f9f1e9d3010f90;json + > ["","lookup","pinger","10.0.0.1:4040#nndKd+gn.a","resolved"] + =head1 SEE ALSO L.