Using Crypt::DSA

Discussion in 'Perl Misc' started by Mike Friedman, Aug 21, 2005.

  1. I'm looking at Crypt::DSA, in particular the docs from
    CPAN. One thing I can't seem to grasp from the documentation:

    How do I extract (and write to disk) just the public key, after
    I've generated a DSA key object? Unlike Crypt::RSA, where keygen
    returns a list consisting of the public and private keys as
    separate scalars, it seems that the DSA keygen method returns
    only a single scalar, which supposedly contains both the public
    and private portions of the key.

    Now, the only way I see to write out a key is with the 'key->write'
    method, but when I use $key->write (where '$key' is what's returned
    from keygen), I get just one object written and it's labeled
    (internally) as a private key.

    What if I want to create a file containing just the public portion,
    so I can distribute it?

    I must admit that the documentation for Crypt::DSA::Key, which
    should explain this, is not at all clear to me. The syntax for
    Crypt::DSA is supposed to be modeled after that of Crypt::RSA,
    but in this area there's clearly a difference and I don't
    understand it.

    Thanks.

    Mike
     
    Mike Friedman, Aug 21, 2005
    #1
    1. Advertising

  2. Mike Friedman <> wrote in news:deb031$il3$1
    @agate.berkeley.edu:

    > How do I extract (and write to disk) just the public key,


    ....

    > I must admit that the documentation for Crypt::DSA::Key, which
    > should explain this, is not at all clear to me.


    I have never used the said modules, but, looking at the documentation:

    <URL:http://search.cpan.org/~btrott/Crypt-DSA-0.13/lib/Crypt/DSA/Key.pm>

    Any of the key attributes can be accessed through combination get/set
    methods. The key attributes are: p, q, g, priv_key, and pub_key. For
    example:

    $key->p($p);
    my $p2 = $key->p;
    ....

    So, I would try

    printf "Public key: %s\n", $key->pub_key;

    to print the public key.

    Sinan
    --
    A. Sinan Unur <>
    (reverse each component and remove .invalid for email address)

    comp.lang.perl.misc guidelines on the WWW:
    http://mail.augustmail.com/~tadmc/clpmisc/clpmisc_guidelines.html
     
    A. Sinan Unur, Aug 21, 2005
    #2
    1. Advertising

  3. A. Sinan Unur <> wrote:
    >
    > Any of the key attributes can be accessed through combination get/set
    > methods. The key attributes are: p, q, g, priv_key, and pub_key. For
    > example:
    >
    > $key->p($p);
    > my $p2 = $key->p;
    > ...
    >
    > So, I would try
    >
    > printf "Public key: %s\n", $key->pub_key;
    >
    > to print the public key.
    >
    > Sinan


    Sinan,

    I had already tried that and, indeed, it might be technically
    correct. But what I really want is an output file that is ASN.1
    encoded and in a standard PEM key format, which is what you get
    when you use the key->write method. It's just that the latter
    seems to give only a private key file (which may actually include
    the public key, but that's just speculation on my part).

    Mike
     
    Mike Friedman, Aug 22, 2005
    #3
  4. Mike Friedman <> wrote in news:deb1u2
    $j2j$:

    > A. Sinan Unur <> wrote:
    >>
    >> Any of the key attributes can be accessed through combination get/set
    >> methods. The key attributes are: p, q, g, priv_key, and pub_key. For
    >> example:
    >>
    >> $key->p($p);
    >> my $p2 = $key->p;
    >> ...
    >>
    >> So, I would try
    >>
    >> printf "Public key: %s\n", $key->pub_key;
    >>
    >> to print the public key.
    >>
    >> Sinan

    ....

    > I had already tried that and, indeed, it might be technically
    > correct. But what I really want is an output file that is ASN.1
    > encoded and in a standard PEM key format, which is what you get
    > when you use the key->write method. It's just that the latter
    > seems to give only a private key file (which may actually include
    > the public key, but that's just speculation on my part).


    Hmmm ... Sorry. I misunderstood the question.

    Unfortunately, I don't know the answer to your question. Good luck.

    Sinan

    --
    A. Sinan Unur <>
    (reverse each component and remove .invalid for email address)

    comp.lang.perl.misc guidelines on the WWW:
    http://mail.augustmail.com/~tadmc/clpmisc/clpmisc_guidelines.html
     
    A. Sinan Unur, Aug 22, 2005
    #4
  5. Mike Friedman

    Sisyphus Guest

    "Mike Friedman" <> wrote in message
    > >
    > > So, I would try
    > >
    > > printf "Public key: %s\n", $key->pub_key;
    > >
    > > to print the public key.
    > >
    > > Sinan

    >
    > Sinan,
    >
    > I had already tried that and, indeed, it might be technically
    > correct. But what I really want is an output file that is ASN.1
    > encoded and in a standard PEM key format, which is what you get
    > when you use the key->write method. It's just that the latter
    > seems to give only a private key file (which may actually include
    > the public key, but that's just speculation on my part).
    >


    Do you have Convert::pEM installed ? It's not a pre-requisite for
    Crypt::DSA, but it might help you get at the answers you want. I don't
    understand PEM format at all - if you're in the same boat then maybe there's
    something in the openssl/doc/crypto/pem.pod for you.

    Included below is a modified version of '04-pem.t' which is part of the
    Crypt::DSA test suite. It demonstrates that the pem file does indeed hold
    both public and private key information (by extracting that and other
    information from
    the pem file that '04-pem.t' generates).

    Hth.

    Cheers,
    Rob

    # $Id: 04-pem.t,v 1.4 2001/04/22 23:58:39 btrott Exp $

    use strict;

    use Test;
    use Crypt::DSA;
    use Crypt::DSA::Key;

    my $no_pem;
    BEGIN {
    eval "use Convert::pEM;";
    $no_pem = $@;
    if ($no_pem) {
    print "1..0 skipping\n";
    exit;
    }

    plan tests => 12;
    }


    my $keyfile = "./dsa-key.pem";

    my $dsa = Crypt::DSA->new;
    my $key = $dsa->keygen( Size => 512 );
    my $key2;

    skip($no_pem, $key->write( Type => 'PEM', Filename => $keyfile));
    $key2 = Crypt::DSA::Key->new( Type => 'PEM', Filename => $keyfile );
    skip($no_pem, $key->p, $key2->p);
    skip($no_pem, $key->q, $key2->q);
    skip($no_pem, $key->g, $key2->g);
    skip($no_pem, $key->pub_key, $key2->pub_key);
    skip($no_pem, $key->priv_key, $key2->priv_key);

    # There's an option (not used here)
    # to password-protect dsa-key.pem.

    skip($no_pem, $key->write( Type => 'PEM', Filename => $keyfile));
    $key2 = Crypt::DSA::Key->new( Type => 'PEM', Filename => $keyfile);
    skip($no_pem, $key->p, $key2->p);
    skip($no_pem, $key->q, $key2->q);
    skip($no_pem, $key->g, $key2->g);
    skip($no_pem, $key->pub_key, $key2->pub_key);
    skip($no_pem, $key->priv_key, $key2->priv_key);

    #unlink $keyfile;

    print "Public: ", $key->pub_key, "\n";
    print "Private: ", $key->priv_key, "\n";
    print "Total: ", $key->write, "\n";

    # To show that dsa-key.pem does hold both private
    # and public key information :

    my $pem = Convert::pEM->new(
    Name => "DSA PRIVATE KEY",
    ASN => qq(
    DSAPrivateKey SEQUENCE {
    version INTEGER,
    p INTEGER,
    q INTEGER,
    g INTEGER,
    pub_key INTEGER,
    priv_key INTEGER
    }
    ));


    my $pkey = $pem->read(
    Filename => $keyfile
    );

    my %deref = %$pkey;
    for(keys(%deref)) {print "\n$_: $deref{$_}\n"}
    print "\n";

    my %d = %{$deref{DSAPrivateKey}};
    for (keys(%d)) {print "$_ : $d{$_}\n"}
     
    Sisyphus, Aug 22, 2005
    #5
  6. Sisyphus <> wrote:
    >
    > Do you have Convert::pEM installed ? It's not a pre-requisite for
    > Crypt::DSA, but it might help you get at the answers you want. I
    > don't understand PEM format at all - if you're in the same boat
    > then maybe there's something in the openssl/doc/crypto/pem.pod
    > for you.


    I've decided for now that just writing out $key->pub_key is sufficient
    for my purposes (to create a public key file). I can read in that hex
    value and use it to sign a messages.

    But now, I've run into another snag with Crypt::DSA. How in the world
    do I write out a signature to a file, in a format that can be read
    in by another script for the purpose of verifying the signed message?

    I tried taking the signature object created by the sign() method,
    base64 encoding it and writing it out. When I read that in, base64
    decode it and populate a new signature object with it, verify()
    complains, I think because I didn't first serialize the signature
    (into an ASN.1 encoded format).

    The CPAN docs say there's supposed to be a $sig->serialize method
    on a signature object. But it appears this isn't true. Not only
    does my script fail, complaining about the absence of such a method, but
    looking at the source code for Crypt::RSA::Signature seems to reveal
    that there is, in fact, no serialize method defined on a signature object.

    So, once again, assuming I've signed a message, creating a signature
    object, how do I write that signature to a file so it can be used
    as input to a verify script?

    BTW: if I do the sign and verify in the same script, passing the
    signature object returned by the sign() method to the verify() method,
    that works fine.

    I'd really appreciate a pointer on this.

    Thanks.

    Mike
     
    Mike Friedman, Aug 26, 2005
    #6
  7. Mike Friedman

    Sisyphus Guest

    "Mike Friedman" <> wrote in message

    >
    > So, once again, assuming I've signed a message, creating a signature
    > object, how do I write that signature to a file so it can be used
    > as input to a verify script?
    >


    It may simply be that you failed to bless() the key and signature retrieved
    from file. Here's a script that writes to file, then reads from that file
    and verifies the message using information obtained only from that file:

    use strict;
    use warnings;
    use Crypt::DSA;

    my $message = "Je suis l'homme a tete de chou.";
    for (1..4) {$message .= "\n$message"}

    my $dsa = Crypt::DSA->new;
    my $key = $dsa->keygen( Size => 512 );

    my $sig = $dsa->sign( Message => $message, Key => $key );

    # @file contains all of the signature and key information that
    # needs to be written to the file.
    my @file = ($key->{p}, $key->{q}, $key->{g}, $key->{pub_key},
    $sig->{r}, $sig->{s});

    # Save @file and $message to file:
    open(WR, ">msg.txt") or die "Can't open WR:$!";
    for(@file) {print WR $_, "\n"}
    print WR $message;
    close(WR) or die "Can't close WR: $!";

    # Retrieve key, signature and message from file
    open(RD, "msg.txt") or die "Can't open RD:$!";
    my @msg_from_file = <RD>;
    close(RD) or die "Can't close RD: $!";

    my %key_from_file;
    my %sig_from_file;

    $key_from_file{p} = shift(@msg_from_file);
    chomp($key_from_file{p});

    $key_from_file{q} = shift(@msg_from_file);
    chomp($key_from_file{q});

    $key_from_file{g} = shift(@msg_from_file);
    chomp($key_from_file{g});

    $key_from_file{pub_key} = shift(@msg_from_file);
    chomp($key_from_file{pub_key});

    $sig_from_file{r} = shift(@msg_from_file);
    chomp($sig_from_file{r});

    $sig_from_file{s} = shift(@msg_from_file);
    chomp($sig_from_file{s});

    my $message_from_file;
    for(@msg_from_file) {$message_from_file .= $_}

    my $k = \%key_from_file;
    bless($k, "Crypt::DSA::Key");

    my $s = \%sig_from_file;
    bless($s, "Crypt::DSA::Signature");


    # Verify using data read from file:
    my $dsa_verify = Crypt::DSA->new;

    my $verified = $dsa_verify->verify(
    Key => $k,
    Message => $message_from_file,
    Signature => $s
    );

    if($verified) {print "Message verified\n"}
    else {print "Message NOT verified\n"}

    __END__

    Cheers,
    Rob
     
    Sisyphus, Aug 26, 2005
    #7
  8. Mike Friedman

    Sisyphus Guest

    "Sisyphus" <> wrote in message

    >
    > # @file contains all of the signature and key information that
    > # needs to be written to the file.
    > my @file = ($key->{p}, $key->{q}, $key->{g}, $key->{pub_key},
    > $sig->{r}, $sig->{s});
    >


    I was being a little dense there - concentrating on being able to reproduce
    the original objects, rather than what would actually be put in the file.
    You probably don't want to be saving $key->{p}, $key->{q} and $sig->{r} -
    and there's no need to save them to file.

    When it comes to creating the key and signature to verify the message you
    can simply set those 3 hash keys to zero (if you find there's a need to
    specify a value for those hash keys at all).

    Cheers,
    Rob
     
    Sisyphus, Aug 26, 2005
    #8
  9. Sisyphus <> wrote:
    >
    > "Mike Friedman" <> wrote in message
    >
    >>
    >> So, once again, assuming I've signed a message, creating a signature
    >> object, how do I write that signature to a file so it can be used
    >> as input to a verify script?
    >>

    >
    > It may simply be that you failed to bless() the key and signature
    > retrieved from file. Here's a script that writes to file, then reads
    > from that file and verifies the message using information obtained only
    > from that file:
    > ...


    Rob,

    Looking at your sample script (but not yet having tried it), it seems that
    it will work (and is quite educational, in fact). However, I still have a
    concern, which has to do with how I intend to use my scripts.

    I want to be able to create a public key file and a signature in formats
    that can be distributed to a user community whose applications may be
    using other DSA implementations (e.g., Java crypto lib, or other scripting
    languages besides perl). So, it's important that the public key file be
    in a reasonably standard format. Also, I'd be passing the signature as
    a base64-encoded string via a web form field. Once the application
    base64-decodes it, the signature should be in a format easily fed to its
    DSA verify routine.

    I'm thinking of how other products, like SSH, do this sort of thing,
    with my main focus being on how various DSA implementations do signature
    verification.

    Thanks.

    Mike
     
    Mike Friedman, Aug 26, 2005
    #9
  10. Mike Friedman

    Sisyphus Guest

    "Mike Friedman" <> wrote in message

    [snip]

    >
    > I want to be able to create a public key file and a signature in formats
    > that can be distributed to a user community whose applications may be
    > using other DSA implementations (e.g., Java crypto lib, or other scripting
    > languages besides perl). So, it's important that the public key file be
    > in a reasonably standard format.


    Which, afaik, would be a pem file. Crypt::DSA can parse them (as long as you
    have Convert::pEM) and so, presumably, can the other DSA implementations. To
    write a *public* key pem file with Crypt::DSA you just do (as in 04-pem.t):

    $key->priv_key(undef);
    $key->write( Type => 'PEM', Filename => $keyfile);


    > Also, I'd be passing the signature as
    > a base64-encoded string via a web form field. Once the application
    > base64-decodes it, the signature should be in a format easily fed to its
    > DSA verify routine.
    >


    Are you saying the signature part is not a problem ? I find some ambiguity
    with "the signature should be in a format easily fed to its DSA verify
    routine" - not sure whether that means the signature is already in a
    suitable format, or whether it means that it needs to be in a suitable
    format (but isn't).

    Cheers,
    Rob
     
    Sisyphus, Aug 27, 2005
    #10
  11. Sisyphus <> wrote:
    >
    > "Mike Friedman" <> wrote in message
    >
    >> I want to be able to create a public key file and a signature in
    >> formats that can be distributed to a user community whose
    >> applications may be using other DSA implementations (e.g., Java
    >> crypto lib, or other scripting languages besides perl). So, it's
    >> important that the public key file be in a reasonably standard format.

    >
    > Which, afaik, would be a pem file. Crypt::DSA can parse them (as
    > long as you have Convert::pEM) and so, presumably, can the other
    > DSA implementations. To write a *public* key pem file with
    > Crypt::DSA you just do (as in 04-pem.t):
    >
    > $key->priv_key(undef);
    > $key->write( Type => 'PEM', Filename => $keyfile);


    Rob,

    I do have Convert::pEM.

    I tried your suggestion above, but I get only a private key. In fact,
    my script first writes the key object (using $key->write) returned
    from sign(), as-is, to one file, then does the above in attempt to
    write the *public* key to another file. But both files are identical!

    Here's the entirety of my little test script:

    ------------------------------------------
    #!/usr/local/bin/perl

    use Crypt::DSA;
    use strict;

    my $dsa = new Crypt::DSA;
    my $key = Crypt::DSA::Key->new;
    my $filename;

    $filename = "./dsakey";

    $key = $dsa->keygen (
    Size => 1024,
    Verbosity => 1,
    ) or die $dsa->errstr();

    $key->write(
    Type => 'PEM',
    Filename => "$filename.priv",
    );

    $key->priv_key(undef);

    $key->write(
    Type => 'PEM',
    Filename => "$filename.pub",
    );

    exit;
    ------------------------------------------

    The result is that 'dsakey.priv' and 'dsakey.pub' have exactly
    the same contents, including the 'BEGIN DSA PRIVATE KEY',
    'END DSA PRIVATE KEY' delimiters, even though I've undefined priv_key.
    What am I doing wrong?

    >> Also, I'd be passing the signature as a base64-encoded string
    >> via a web form field. Once the application base64-decodes it,
    >> the signature should be in a format easily fed to its DSA
    >> verify routine.

    >
    > Are you saying the signature part is not a problem ? I find some
    > ambiguity with "the signature should be in a format easily fed to
    > its DSA verify routine" - not sure whether that means the signature
    > is already in a suitable format, or whether it means that it needs
    > to be in a suitable format (but isn't).


    I was just expressing my concern that the signature as produced in your
    earlier example (by writing out the separate components of the signature
    object) wouldn't be 'standard' in some sense. But I haven't yet gotten
    far enough to try all that out.

    Right now, I'm interested in your idea of how to write out a public
    key file in PEM format; it seems it should work, but I'm not getting
    the right results. So, probably I'm overlooking something obvious.

    Thanks for your help so far.

    Mike
     
    Mike Friedman, Aug 27, 2005
    #11
  12. Mike Friedman

    Sisyphus Guest

    "Mike Friedman" <> wrote in message

    >
    > Here's the entirety of my little test script:
    >
    > ------------------------------------------
    > #!/usr/local/bin/perl
    >
    > use Crypt::DSA;
    > use strict;
    >
    > my $dsa = new Crypt::DSA;
    > my $key = Crypt::DSA::Key->new;
    > my $filename;
    >
    > $filename = "./dsakey";
    >
    > $key = $dsa->keygen (
    > Size => 1024,
    > Verbosity => 1,
    > ) or die $dsa->errstr();
    >
    > $key->write(
    > Type => 'PEM',
    > Filename => "$filename.priv",
    > );
    >
    > $key->priv_key(undef);
    >
    > $key->write(
    > Type => 'PEM',
    > Filename => "$filename.pub",
    > );
    >
    > exit;
    > ------------------------------------------
    >
    > The result is that 'dsakey.priv' and 'dsakey.pub' have exactly
    > the same contents, including the 'BEGIN DSA PRIVATE KEY',
    > 'END DSA PRIVATE KEY' delimiters, even though I've undefined priv_key.
    > What am I doing wrong?
    >


    Hmmm ... I run a copy'n'paste of that script and get 2 different files. The
    '.priv' file has 'BEGIN DSA PRIVATE KEY', 'END DSA PRIVATE KEY' delimiters
    and the '.pub' file has 'BEGIN PUBLIC KEY', 'END PUBLIC KEY' delimiters. The
    base64 content is also different. Are we using the same version of
    Crypt::DSA ? (I've got 0.13 which, I think, is the latest.)

    I'm on Win32 - but I don't think that's going to account for the different
    behaviour (in this instance :)

    Other than that, check 'perldoc Crypt::DSA::Key' for some possible clues.

    Cheers,
    Rob
     
    Sisyphus, Aug 27, 2005
    #12
  13. Sisyphus <> wrote:
    >
    > Hmmm ... I run a copy'n'paste of that script and get 2 different files. The
    > '.priv' file has 'BEGIN DSA PRIVATE KEY', 'END DSA PRIVATE KEY' delimiters
    > and the '.pub' file has 'BEGIN PUBLIC KEY', 'END PUBLIC KEY' delimiters. The
    > base64 content is also different. Are we using the same version of
    > Crypt::DSA ? (I've got 0.13 which, I think, is the latest.)


    Rob,

    I'm using version 0.12_1, which represents the latest FreeBSD port. I
    prefer to install from ports because, especially in the case of software
    that has lots of dependencies, installing manually becomes a real headache.
    (A while back, when I had a Solaris box, I tried to install Crypt::RSA and
    I could never, it seems, get to the end of installing all the dependencies!).

    I see from CPAN that there have been some changes between 0.12 and 0.13, some
    of which may affect what I've been doing (and asking about). For example,
    I earlier inquired about the $sig->serialize method and it turns out that
    was introduced in 0.13.

    Anyway, there doesn't seem to be a port available yet for 0.13. Since I'm
    just doing prototype testing, for an application that probably won't even be
    running on FreeBSD, I don't want to get bogged down in version discrepancies.
    I may just wait for a while to decide what to do.

    Thanks.

    Mike
     
    Mike Friedman, Aug 27, 2005
    #13
  14. Mike Friedman

    Sisyphus Guest

    "Mike Friedman" <> wrote in message
    news:deqmr0$1j2t$...
    > Sisyphus <> wrote:
    > >
    > > Hmmm ... I run a copy'n'paste of that script and get 2 different files.

    The
    > > '.priv' file has 'BEGIN DSA PRIVATE KEY', 'END DSA PRIVATE KEY'

    delimiters
    > > and the '.pub' file has 'BEGIN PUBLIC KEY', 'END PUBLIC KEY' delimiters.

    The
    > > base64 content is also different. Are we using the same version of
    > > Crypt::DSA ? (I've got 0.13 which, I think, is the latest.)

    >
    > Rob,
    >
    > I'm using version 0.12_1, which represents the latest FreeBSD port. I
    > prefer to install from ports because, especially in the case of software
    > that has lots of dependencies, installing manually becomes a real

    headache.
    > (A while back, when I had a Solaris box, I tried to install Crypt::RSA and
    > I could never, it seems, get to the end of installing all the

    dependencies!).

    Yep - either use the CPAN module which "does it all" for you ...... or just
    persevere. Most people use CPAN, but I prefer to use perseverance :)

    >
    > I see from CPAN that there have been some changes between 0.12 and 0.13,

    some
    > of which may affect what I've been doing (and asking about). For example,
    > I earlier inquired about the $sig->serialize method and it turns out that
    > was introduced in 0.13.


    Looking at the documentation for Crypt::DSA::Key in version 0.12, I see *no*
    mention of being able to write just the public portion of the key to file.
    Perhaps it can't be done. Odd that doing '$key->priv_key(undef);' had no
    effect on the file that was written. I guess that instruction must be being
    ignored on 0.12. (Perhaps if you had used the warnings pragma in that script
    you posted, some mention of that might have been made :)

    >
    > Anyway, there doesn't seem to be a port available yet for 0.13. Since I'm
    > just doing prototype testing, for an application that probably won't even

    be
    > running on FreeBSD, I don't want to get bogged down in version

    discrepancies.
    > I may just wait for a while to decide what to do.
    >


    I just noticed a module called Crypt::OpenSSL::DSA which is a DSA
    implementation that uses OpenSSL. If you have OpenSSL, you may get better
    milage with that module.

    Cheers,
    Rob
     
    Sisyphus, Aug 28, 2005
    #14
  15. Sisyphus <> wrote:
    >
    > I just noticed a module called Crypt::OpenSSL::DSA which is a DSA
    > implementation that uses OpenSSL. If you have OpenSSL, you may get better
    > milage with that module.


    Rob,

    Now that's the best advice I've received in a long time! In just about
    15 minutes, I installed Crypt::OpenSSL::DSA (the current version, 0.12)
    from a FreeBSD port and created 3 scripts: to generate and write out a
    public and private key (each to a PEM format file), to sign a message
    and to verify a message.

    The interface is so much simpler than with Crypt::DSA.

    Thanks very much. I now have something to work with.

    Mike
    >
    >
     
    Mike Friedman, Aug 28, 2005
    #15
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. AdrianK
    Replies:
    0
    Views:
    1,551
    AdrianK
    Jul 9, 2003
  2. Replies:
    2
    Views:
    4,940
  3. Adam Tauno Williams
    Replies:
    2
    Views:
    891
    Stefan Behnel
    Dec 30, 2010
  4. Cosmia Luna
    Replies:
    4
    Views:
    339
    Cosmia Luna
    Mar 11, 2012
  5. asg

    de-crypt... crypt

    asg, Dec 23, 2005, in forum: Perl Misc
    Replies:
    3
    Views:
    140
Loading...

Share This Page