Mail-DKIM-Iterator

 view release on metacpan or  search on metacpan

lib/Mail/DKIM/Iterator.pm  view on Meta::CPAN

	push @nh,($_) x $oh{$_} for keys %oh;
	$sig->{h} = join(':',@nh);
    }
    $sig = parse_signature($sig,$error,1) or return;


    my %sig = %$sig;
    $sig{t} = time() if !$sig{t} && exists $sig{t};
    $sig{x} = ($sig{t} || time()) + $1
	if $sig{x} && $sig{x} =~m{^\+(\d+)$};
    $sig{'a:key'} eq 'rsa' or do {
	$$error = "unsupported algorithm ".$sig{'a:key'};
	return;
    };
    delete $sig{b};
    $sig{i} = _encodeQP($sig{':i'}) if $sig{':i'};
    $sig{z} = _encodeQP($sig{':z'}) if $sig{':z'};
    $sig{bh} = _encode64($sig{'bh:computed'} || $sig{'bh:bin'});
    $sig{h} = join(':',@{$sig{'h:list'}});

    my @v;
    for (qw(v a c d q s t x h l i z bh)) {
	my $v = delete $sig{$_} // next;
	push @v, "$_=$v"
    }
    for(sort keys %sig) {
	m{:} and next;
	my $v = _encodeQP(delete $sig{$_} // next);
	push @v, "$_=$v"
    }

    my @lines = shift(@v);
    for(@v,"b=") {
	$lines[-1] .= ';';
	my $append = " $_";
	my $x80 = (@lines == 1 ? 64 : 80) - length($lines[-1]);
	if (length($append)<=$x80) {
	    $lines[-1] .= $append;
	} elsif (length($append)<=80) {
	    push @lines,$append;
	} else {
	    while (1) {
		if ( $x80>10) {
		    $lines[-1] .= substr($append,0,$x80,'');
		    $append eq '' and last;
		}
		push @lines,' ';
		$x80 = 80;
	    }
	}
    }

    my $dkh = 'DKIM-Signature: '.join("\r\n",@lines);
    $sig->{'a:key'} eq 'rsa' or do {
	$$error = "unsupported signature algorithm $sig->{'a:key'}";
	return;
    };
    my $hash = _compute_hdrhash($hdr,
	$sig{'h:list'},$sig->{'a:hash'},$sig->{'c:hdr'},$dkh);

    my $priv = ref($key) ? $key : Crypt::OpenSSL::RSA->new_private_key($key);
    $priv or do {
	$$error = "using private key failed";
	return;
    };
    $priv->use_no_padding;

    my $data = _encode64($priv->decrypt(
	_emsa_pkcs1_v15($sig->{'a:hash'},$hash,$priv->size)));

    my $x80 = 80 - ($dkh =~m{\n([^\n]+)\z} && length($1));
    while ($data ne '') {
	$dkh .= substr($data,0,$x80,'') if $x80>10;
	$dkh .= "\r\n " if $data ne '';
	$x80 = 80;
    }
    $dkh .= "\r\n";
    return $dkh;
}

# Verify a DKIM signature (hash from parse_signature) using a DKIM key (hash
# from parse_dkimkey). Output is (error_code,error_string) or simply
# (DKIM_PASS) in case of no error or () if no final result can be computed yet.
sub _verify_sig {
    my ($sig,$param) = @_;

    # check pre-computed hash over body if body done
    if (defined $sig->{'bh:computed'}
	and $sig->{'bh:computed'} ne $sig->{'bh:bin'}) {
	return (DKIM_FAIL, 'body hash mismatch');
    }
    return if ! $param;

    return (DKIM_PERMERROR,"none or invalid dkim record") if ! %$param;
    return (DKIM_TEMPERROR,$param->{tempfail}) if $param->{tempfail};
    return (DKIM_PERMERROR,$param->{permfail}) if $param->{permfail};

    my $FAIL = $param->{t}{y} ? DKIM_NEUTRAL : DKIM_FAIL;
    return ($FAIL,"key revoked") if ! $param->{p};

    return ($FAIL,"hash algorithm not allowed")
	if ! $param->{h}{$sig->{'a:hash'}};

    return ($FAIL,"identity does not match domain") if $param->{t}{s}
	&& $sig->{'i:domain'} && $sig->{'i:domain'} ne $sig->{d};

    return ($FAIL,"identity does not match granularity")
	if $param->{g} && $sig->{i} !~ $param->{g};

    # needs bh:computed to continue
    return if ! defined $sig->{'bh:computed'};

    if (!eval {
	my $rsa = Crypt::OpenSSL::RSA->new_public_key(do {
	    local $_ = $param->{p};
	    s{\s+}{}g;
	    s{(.{1,64})}{$1\n}g;
	    "-----BEGIN PUBLIC KEY-----\n$_-----END PUBLIC KEY-----\n";
	}) or die [DKIM_PERMERROR,"using public key failed"];
	$rsa->use_no_padding;
	my $bencrypt = eval { $rsa->encrypt($sig->{'b:bin'}) }



( run in 1.505 second using v1.01-cache-2.11-cpan-13bb782fe5a )