# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
#
# Copyright (C) 2018-2019 Regents of the University of California.
# Author: Jeff Thompson <[email protected]>
# Author: From ndn-cxx security https://github.com/named-data/ndn-cxx/blob/master/ndn-cxx/security/verification-helpers.cpp
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# A copy of the GNU Lesser General Public License is in the file COPYING.
"""
This module defines the VerificationHelpers which has static methods to verify
signatures and digests.
"""
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import padding, ec
from cryptography.hazmat.primitives.serialization import load_der_public_key
from cryptography.hazmat.primitives import hashes
from pyndn.security.security_types import DigestAlgorithm
from pyndn.security.certificate.public_key import PublicKey
from pyndn.security.security_types import KeyType
from pyndn.util.blob import Blob
from pyndn.encoding.wire_format import WireFormat
from pyndn.security.v2.certificate_v2 import CertificateV2
[docs]class VerificationHelpers(object):
[docs] @staticmethod
def verifySignature(buffer, signature, publicKey,
digestAlgorithm = DigestAlgorithm.SHA256):
"""
Verify the buffer against the signature using the public key.
:param buffer: The input buffer to verify.
:type buffer: Blob or an object which is the same as the bytes() operator
:param signature: The signature bytes.
:type signature: Blob or an object which is the same as the bytes()
operator
:param publicKey: The object containing the public key, or the public
key DER which is used to make the PublicKey object.
:type publicKey: PublicKey or Blob or an object which is the same as
the bytes() operator
:param digestAlgorithm: (optional) The digest algorithm. If omitted, use
DigestAlgorithm.SHA256.
:type digestAlgorithm: int from DigestAlgorithm
:return: True if verification succeeds, False if verification fails.
:rtype: bool
:raises: ValueError for an invalid public key type or digestAlgorithm.
"""
if digestAlgorithm == None:
digestAlgorithm = DigestAlgorithm.SHA256
if isinstance(buffer, Blob):
buffer = buffer.toBytes()
if isinstance(signature, Blob):
signature = signature.toBytes()
if not isinstance(publicKey, PublicKey):
# Turn publicKey into a PublicKey object.
if not isinstance(publicKey, Blob):
publicKey = Blob(publicKey)
publicKey = PublicKey(publicKey)
if digestAlgorithm == DigestAlgorithm.SHA256:
if publicKey.getKeyType() == KeyType.RSA:
# Get the public key.
try:
cryptoPublicKey = load_der_public_key(
publicKey.getKeyDer().toBytes(),
backend = default_backend())
except:
return False
try:
cryptoPublicKey.verify(
signature, buffer, padding.PKCS1v15(), hashes.SHA256())
return True
except:
return False
elif publicKey.getKeyType() == KeyType.EC:
# Get the public key.
try:
cryptoPublicKey = load_der_public_key(
publicKey.getKeyDer().toBytes(),
backend = default_backend())
except:
return False
try:
cryptoPublicKey.verify(
signature, buffer, ec.ECDSA(hashes.SHA256()))
return True
except:
return False
else:
raise ValueError("verifySignature: Invalid key type")
else:
raise ValueError("verifySignature: Invalid digest algorithm")
[docs] @staticmethod
def verifyDataSignature(
data, publicKeyOrCertificate, digestAlgorithm = None, wireFormat = None):
"""
Verify the Data packet using the public key. This does not check the
type of public key or digest algorithm against the type of SignatureInfo
in the Data packet such as Sha256WithRsaSignature.
:param Data data: The Data packet to verify.
:param publicKeyOrCertificate: The object containing the public key, or
the public key DER which is used to make the PublicKey object, or the
certificate containing the public key.
:type publicKeyOrCertificate: Blob, or an object which is the same as
the bytes() operator, or CertificateV2
:param digestAlgorithm: (optional) The digest algorithm. If omitted, use
DigestAlgorithm.SHA256.
:param WireFormat wireFormat: (optional) A WireFormat object used to
encode the Data packet. If omitted, use
WireFormat.getDefaultWireFormat().
:raises: ValueError for an invalid public key type or digestAlgorithm.
"""
arg3 = digestAlgorithm
arg4 = wireFormat
if type(arg3) is int:
digestAlgorithm = arg3
else:
digestAlgorithm = None
if isinstance(arg3, WireFormat):
wireFormat = arg3
elif isinstance(arg4, WireFormat):
wireFormat = arg4
else:
wireFormat = None
if isinstance(publicKeyOrCertificate, CertificateV2):
try:
publicKey = publicKeyOrCertificate.getPublicKey()
except:
return False
else:
publicKey = publicKeyOrCertificate;
encoding = data.wireEncode(wireFormat)
return VerificationHelpers.verifySignature(
encoding.toSignedBytes(), data.getSignature().getSignature(),
publicKey, digestAlgorithm)
[docs] @staticmethod
def verifyInterestSignature(
interest, publicKeyOrCertificate, digestAlgorithm = None, wireFormat = None):
"""
Verify the Interest packet using the public key, where the last two name
components are the SignatureInfo and signature bytes. This does not
check the type of public key or digest algorithm against the type of
SignatureInfo such as Sha256WithRsaSignature.
:param Interest interest: The Interest packet to verify.
:param publicKeyOrCertificate: The object containing the public key, or
the public key DER which is used to make the PublicKey object, or the
certificate containing the public key.
:type publicKeyOrCertificate: Blob, or an object which is the same as
the bytes() operator, or CertificateV2
:param digestAlgorithm: (optional) The digest algorithm. If omitted, use
DigestAlgorithm.SHA256.
:param WireFormat wireFormat: (optional) A WireFormat object used to
encode the Interest packet. If omitted, use
WireFormat.getDefaultWireFormat().
:raises: ValueError for an invalid public key type or digestAlgorithm.
"""
arg3 = digestAlgorithm
arg4 = wireFormat
if type(arg3) is int:
digestAlgorithm = arg3
else:
digestAlgorithm = None
if isinstance(arg3, WireFormat):
wireFormat = arg3
elif isinstance(arg4, WireFormat):
wireFormat = arg4
else:
wireFormat = None
if isinstance(publicKeyOrCertificate, CertificateV2):
try:
publicKey = publicKeyOrCertificate.getPublicKey()
except:
return False
else:
publicKey = publicKeyOrCertificate;
if wireFormat == None:
wireFormat = WireFormat.getDefaultWireFormat()
signature = VerificationHelpers._extractSignature(interest, wireFormat)
if signature == None:
return False
encoding = interest.wireEncode(wireFormat)
return VerificationHelpers.verifySignature(
encoding.toSignedBytes(), signature.getSignature(), publicKey,
digestAlgorithm)
[docs] @staticmethod
def verifyDigest(buffer, digest, digestAlgorithm):
"""
Verify the buffer against the digest using the digest algorithm.
:param buffer: The input buffer to verify.
:type buffer: Blob or an object which is the same as the bytes() operator
:param digest: The digest bytes.
:type digest: Blob or an object which is the same as the bytes() operator
:param digestAlgorithm: The digest algorithm.
:type digestAlgorithm: int from DigestAlgorithm
:return: True if verification succeeds, False if verification fails.
:rtype: bool
:raises: ValueError for an invalid digestAlgorithm.
"""
if isinstance(buffer, Blob):
buffer = buffer.toBytes()
if isinstance(digest, Blob):
digest = digest.toBytes()
if digestAlgorithm == DigestAlgorithm.SHA256:
# Get the hash of the bytes to verify.
sha256 = hashes.Hash(hashes.SHA256(), backend=default_backend())
sha256.update(buffer)
bufferDigest = sha256.finalize()
return digest == bufferDigest
else:
raise ValueError("verifyDigest: Invalid digest algorithm")
@staticmethod
def _extractSignature(interest, wireFormat):
"""
Extract the signature information from the interest name.
:param Interest interest: The interest whose signature is needed.
:param WireFormat wireFormat: The wire format used to decode signature
information from the interest name.
:return: The Signature object, or None if can't decode.
:rtype: Signature
"""
if interest.getName().size() < 2:
return None
try:
return wireFormat.decodeSignatureInfoAndValue(
interest.getName().get(-2).getValue().buf(),
interest.getName().get(-1).getValue().buf(), False)
except:
return None