Search code examples
pythonpython-3.xpicklenamedtuplepython-cryptography

How to pickle namedtuple subclass with RSAPublicKey field


I'm trying to serialize objects that subclass namedtuple and hold references to RSAPublicKey objects.

  • I subclass a namedtuple to create immutable object instances.

  • One of the fields of my class holds a reference to an object of type RSAPublicKey.

  • I define __setstate__(self) to serialize the RSAPublicKey object for pickling.

class implementation

from collections import namedtuple

class MyClass(namedtuple('MyTuple', ['pub_key', 'foo'])):
    def __getstate__(self):
        return {
            'pub_key': serialize(self.pub_key),
            'foo': self.foo
        }

    def __repr__(self):
        return f'MyClass({serialize(self.pub_key)}, {self.foo})'

in use

import pickle

pr, pu = generate_keys()
my_obj = MyClass(pu, 4)

key generation and serialization

from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend

def generate_keys():
    priv = rsa.generate_private_key(
        public_exponent=65537,
        key_size=2048,
        backend=default_backend()
    )
    return priv, priv.public_key()

def serialize(key):
    return key.public_bytes(
        serialization.Encoding.PEM,
        serialization.PublicFormat.SubjectPublicKeyInfo
    )

I get the following error.

TypeError                                 Traceback (most recent call last)
<ipython-input-14-cb7f2cfd2a4d> in <module>
      1 with open('save.dat', 'wb') as f:
----> 2     pickle.dump(my_obj, f)

TypeError: can't pickle CompiledFFI objects

When I change my class to subclass object, the serialization works correctly.

class MyClass():
    def __init__(self, pub_key, foo):
        self.pub_key = pub_key
        self.foo = foo

    def __getstate__(self):
        return {
            'pub_key': serialize(self.pub_key),
            'foo': self.foo
        }

    def __repr__(self):
        return f'MyClass({serialize(self.pub_key)}, {self.foo})'

Solution

  • this will work:

    from collections import namedtuple
    
    class MyClass(namedtuple('MyTuple', ['pub_key', 'foo'])):
        def __getstate__(self):
            return {
                'pub_key': serialize(self.pub_key),
                'foo': self.foo
            }
    
        def __repr__(self):
            return f'MyClass({serialize(self.pub_key)}, {self.foo})'
    
    
    
    
    #key generation and serialization
    
    from cryptography.hazmat.primitives.asymmetric import rsa
    from cryptography.hazmat.primitives import serialization
    from cryptography.hazmat.backends import default_backend
    
    def generate_keys():
        priv = rsa.generate_private_key(
            public_exponent=65537,
            key_size=2048,
            backend=default_backend()
        )
        return priv, priv.public_key()
    
    def serialize(key):
        return key.public_bytes(
            serialization.Encoding.PEM,
            serialization.PublicFormat.SubjectPublicKeyInfo
        )
    import pickle
    #dumping
    pr, pu = generate_keys()
    my_obj = MyClass(pu, 4)
    
    with open('save.dat', 'wb') as f:
        #getting the data from the class
        pickle.dump(my_obj.__getstate__(), f)
    

    it is the same but i use getstate to get the data and not the whole class