our
$VERSION
=
"0.88"
;
sub
new {
my
$type
=
shift
;
my
%prms
=
@_
;
my
$self
= {};
$self
->{
'ALGO'
} =
$prms
{
'Algorithm'
};
$self
->{
'DATA'
} =
$prms
{
'Signature'
};
$self
->{
'METH'
} =
$prms
{
'Method'
};
$self
->{
'DOMN'
} =
$prms
{
'Domain'
};
$self
->{
'HDRS'
} =
$prms
{
'Headers'
};
$self
->{
'PROT'
} =
$prms
{
'Query'
};
$self
->{
'SLCT'
} =
$prms
{
'Selector'
};
$self
->{
'SHDR'
} =
$prms
{
'SignHeaders'
};
$self
->{
'SIGN'
} =
$prms
{
'Signing'
};
$self
->{
'CFWS'
} =
$prms
{
'FWS'
};
bless
$self
,
$type
;
}
sub
parse {
my
$type
=
shift
;
my
%prms
=
@_
;
my
$self
= {};
foreach
my
$tag
(
split
/;/,
$prms
{
"String"
}) {
$tag
=~ s/^\s*|\s*$//g;
foreach
(
$tag
) {
/^a=(rsa-sha1)$/i and
$self
->{
'ALGO'
} =
lc
$1;
/^b=([A-Za-z0-9\+\/\=\s]+)$/ and
$self
->{
'DATA'
} = $1;
/^c=(nofws|simple)$/i and
$self
->{
'METH'
} =
lc
$1;
/^d=([A-Za-z0-9\-\.]+)$/ and
$self
->{
'DOMN'
} =
lc
$1;
/^h=(.*)$/s and
$self
->{
'HDRS'
} =
lc
$1;
/^
q=(dns)$/i and
$self->{'PROT'} =
lc
$1;
/^s=(\S+)$/ and
$self
->{
'SLCT'
} = $1;
}
}
bless
$self
,
$type
;
}
sub
wantheader {
my
$self
=
shift
;
my
$attr
=
shift
;
if
(
$self
->signheaderlist) {
foreach
my
$key
(
$self
->signheaderlist) {
lc
$attr
eq
lc
$key
and
return
1;
}
return
;
}
if
(
$self
->headerlist) {
foreach
my
$key
(
$self
->headerlist) {
lc
$attr
eq
lc
$key
and
return
1;
}
return
;
}
return
1;
}
sub
as_string {
my
$self
=
shift
;
my
$text
;
$self
->algorithm and
$text
.=
"a="
.
$self
->algorithm .
"; "
;
$self
->headerlist and
$text
.=
"h="
.
$self
->headerlist .
"; "
;
$text
.=
"q="
.
$self
->protocol .
"; "
;
$text
.=
"c="
.
$self
->method .
"; "
;
$text
.=
"s="
.
$self
->selector .
"; "
;
$text
.=
"d="
.
$self
->domain .
"; "
;
$text
.=
"b="
.
$self
->signature;
if
(
defined
(
my
$cfws
=
$self
->fws)) {
local
$Text::Wrap::columns
= 78;
$text
= Text::Wrap::wrap(
""
,
$cfws
,
$text
);
$text
.=
"\n"
;
}
return
$text
;
}
sub
sign {
my
$self
=
shift
;
my
%prms
=
@_
;
$self
->method(
$prms
{
'Method'
})
if
$prms
{
'Method'
};
$self
->selector(
$prms
{
'Selector'
})
if
$prms
{
'Selector'
};
$self
->private(
$prms
{
'Private'
})
if
$prms
{
'Private'
};
my
$text
=
$prms
{
'Text'
} or
$self
->errorstr(
"no text given"
),
return
;
$self
->method or
$self
->errorstr(
"no method specified"
),
return
;
$self
->private or
$self
->errorstr(
"no private key specified"
),
return
;
$self
->selector or
$self
->errorstr(
"no selector specified"
),
return
;
$self
->domain or
$self
->errorstr(
"no domain specified"
),
return
;
$self
->protocol or
$self
->protocol(
"dns"
);
$self
->algorithm or
$self
->algorithm(
"rsa-sha1"
);
my
$signing_domain
=
$self
->domain;
$prms
{
'Sender'
}->host =~ /(^|\.)\Q
$signing_domain
\E\z/i or
$self
->errorstr(
"domain does not match address"
),
return
;
my
$sign
=
$self
->private->sign(
$text
);
my
$signb64
= encode_base64(
$sign
,
""
);
$self
->signature(
$signb64
);
$self
->status(
"good"
);
return
1;
}
sub
verify {
my
$self
=
shift
;
my
%prms
=
@_
;
$self
->status(
"bad format"
),
$self
->selector or
$self
->errorstr(
"no selector specified"
),
return
;
$self
->domain or
$self
->errorstr(
"no domain specified"
),
return
;
unless
(
$self
->public) {
my
$pubk
= fetch Mail::DomainKeys::Key::Public(
Protocol
=>
$self
->protocol,
Selector
=>
$self
->selector,
Domain
=>
$self
->domain) or
$self
->status(
"no key"
),
$self
->errorstr(
"no public key available"
),
return
;
$pubk
->revoked and
$self
->status(
"revoked"
),
$self
->errorstr(
"public key has been revoked"
),
return
;
$self
->public(
$pubk
);
}
$self
->status(
"bad"
);
my
$signing_domain
=
$self
->domain;
$prms
{
'Sender'
}->host =~ /(^|\.)\Q
$signing_domain
\E\z/i or
$self
->errorstr(
"domain does not match address"
),
return
;
$prms
{
'Sender'
}->host eq
$self
->domain or
$self
->errorstr(
"domain does not match address"
),
return
;
$self
->public->granularity and
$prms
{
'Sender'
}->user ne
$self
->public->granularity and
$self
->errorstr(
"granularity does not match address"
),
return
;
$self
->public->verify(
Text
=>
$prms
{
'Text'
},
Signature
=> decode_base64(
$self
->signature)) and
$self
->errorstr(
undef
),
$self
->status(
"good"
),
return
1;
$self
->errorstr(
"signature invalid"
);
return
;
}
sub
algorithm {
my
$self
=
shift
;
@_
and
$self
->{
'ALGO'
} =
shift
;
$self
->{
'ALGO'
};
}
sub
domain {
my
$self
=
shift
;
@_
and
$self
->{
'DOMN'
} =
shift
;
$self
->{
'DOMN'
};
}
sub
errorstr {
my
$self
=
shift
;
@_
and
$self
->{
'ESTR'
} =
shift
;
$self
->{
'ESTR'
};
}
sub
fws {
my
$self
=
shift
;
@_
and
$self
->{
'CFWS'
} =
shift
;
return
$self
->{
'CFWS'
};
}
sub
headerlist {
my
$self
=
shift
;
@_
and
$self
->{
'HDRS'
} =
shift
;
if
(
wantarray
and
$self
->{
'HDRS'
}) {
my
@list
=
split
/[ \t]*:[ \t]*/,
$self
->{
'HDRS'
};
return
@list
;
}
$self
->{
'HDRS'
};
}
sub
method {
my
$self
=
shift
;
@_
and
$self
->{
'METH'
} =
shift
;
$self
->{
'METH'
};
}
sub
public {
my
$self
=
shift
;
@_
and
$self
->{
'PBLC'
} =
shift
;
$self
->{
'PBLC'
};
}
sub
private {
my
$self
=
shift
;
@_
and
$self
->{
'PRIV'
} =
shift
;
$self
->{
'PRIV'
};
}
sub
protocol {
my
$self
=
shift
;
@_
and
$self
->{
'PROT'
} =
shift
;
$self
->{
'PROT'
};
}
sub
selector {
my
$self
=
shift
;
@_
and
$self
->{
'SLCT'
} =
shift
;
$self
->{
'SLCT'
};
}
sub
signature {
my
$self
=
shift
;
@_
and
$self
->{
'DATA'
} =
shift
;
$self
->{
'DATA'
};
}
sub
signheaderlist {
my
$self
=
shift
;
@_
and
$self
->{
'SHDR'
} =
shift
;
if
(
wantarray
and
$self
->{
'SHDR'
}) {
my
@list
=
split
/:/,
$self
->{
'SHDR'
};
return
@list
;
}
$self
->{
'SHDR'
};
}
sub
signing {
my
$self
=
shift
;
@_
and
$self
->{
'SIGN'
} =
shift
;
$self
->{
'SIGN'
};
}
sub
status {
my
$self
=
shift
;
@_
and
$self
->{
'STAT'
} =
shift
;
$self
->{
'STAT'
};
}
sub
testing {
my
$self
=
shift
;
$self
->public and
$self
->public->testing and
return
1;
return
;
}
1;