1#!/usr/bin/env python
2# Copyright 2015 Google Inc. All rights reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15"""pyCrypto Crypto-related routines for oauth2client_4_0."""
16
17from Crypto.Hash import SHA256
18from Crypto.PublicKey import RSA
19from Crypto.Signature import PKCS1_v1_5
20from Crypto.Util.asn1 import DerSequence
21
22from oauth2client_4_0 import _helpers
23
24
25class PyCryptoVerifier(object):
26    """Verifies the signature on a message."""
27
28    def __init__(self, pubkey):
29        """Constructor.
30
31        Args:
32            pubkey: OpenSSL.crypto.PKey (or equiv), The public key to verify
33            with.
34        """
35        self._pubkey = pubkey
36
37    def verify(self, message, signature):
38        """Verifies a message against a signature.
39
40        Args:
41            message: string or bytes, The message to verify. If string, will be
42                     encoded to bytes as utf-8.
43            signature: string or bytes, The signature on the message.
44
45        Returns:
46            True if message was signed by the private key associated with the
47            public key that this object was constructed with.
48        """
49        message = _helpers._to_bytes(message, encoding='utf-8')
50        return PKCS1_v1_5.new(self._pubkey).verify(
51            SHA256.new(message), signature)
52
53    @staticmethod
54    def from_string(key_pem, is_x509_cert):
55        """Construct a Verified instance from a string.
56
57        Args:
58            key_pem: string, public key in PEM format.
59            is_x509_cert: bool, True if key_pem is an X509 cert, otherwise it
60                          is expected to be an RSA key in PEM format.
61
62        Returns:
63            Verifier instance.
64        """
65        if is_x509_cert:
66            key_pem = _helpers._to_bytes(key_pem)
67            pemLines = key_pem.replace(b' ', b'').split()
68            certDer = _helpers._urlsafe_b64decode(b''.join(pemLines[1:-1]))
69            certSeq = DerSequence()
70            certSeq.decode(certDer)
71            tbsSeq = DerSequence()
72            tbsSeq.decode(certSeq[0])
73            pubkey = RSA.importKey(tbsSeq[6])
74        else:
75            pubkey = RSA.importKey(key_pem)
76        return PyCryptoVerifier(pubkey)
77
78
79class PyCryptoSigner(object):
80    """Signs messages with a private key."""
81
82    def __init__(self, pkey):
83        """Constructor.
84
85        Args:
86            pkey, OpenSSL.crypto.PKey (or equiv), The private key to sign with.
87        """
88        self._key = pkey
89
90    def sign(self, message):
91        """Signs a message.
92
93        Args:
94            message: string, Message to be signed.
95
96        Returns:
97            string, The signature of the message for the given key.
98        """
99        message = _helpers._to_bytes(message, encoding='utf-8')
100        return PKCS1_v1_5.new(self._key).sign(SHA256.new(message))
101
102    @staticmethod
103    def from_string(key, password='notasecret'):
104        """Construct a Signer instance from a string.
105
106        Args:
107            key: string, private key in PEM format.
108            password: string, password for private key file. Unused for PEM
109                      files.
110
111        Returns:
112            Signer instance.
113
114        Raises:
115            NotImplementedError if the key isn't in PEM format.
116        """
117        parsed_pem_key = _helpers._parse_pem_key(_helpers._to_bytes(key))
118        if parsed_pem_key:
119            pkey = RSA.importKey(parsed_pem_key)
120        else:
121            raise NotImplementedError(
122                'No key in PEM format was detected. This implementation '
123                'can only use the PyCrypto library for keys in PEM '
124                'format.')
125        return PyCryptoSigner(pkey)
126