"""
- Interface to OpenSSL object identifier database
+ Interface to OpenSSL object identifier database.
+ It is primarily intended to deal with OIDs which are compiled into the
+ database or defined in the openssl configuration files.
+
+ But see create() function
+
"""
from ctypescrypto import libcrypto
+from ctypes import c_char_p, c_void_p, c_int, create_string_buffer
class Oid:
+ """
+ Represents an OID. It can be consturucted by textual
+ representation like Oid("commonName") or Oid("CN"),
+ dotted-decimal Oid("1.2.3.4") or using OpenSSL numeric
+ identifer (NID), which is typically returned or required by
+ OpenSSL API functions. If object is consturcted from textual
+ representation which is not present in the database, it fails
+ with ValueError
+
+ attribute nid - contains object nid.
+
+
+ """
+
def __init__(self,value):
+ " Object constuctor. Accepts string or integer"
if type(value) == type(""):
self.nid=libcrypto.OBJ_txt2nid(value)
if self.nid==0:
- raise LibCryptoError("Cannot find object %s in the
- database"%(value))
+ raise ValueError("Cannot find object %s in the database"%(value))
elif type(value) == type(0):
+ cn=libcrypto.OBJ_nid2sn(value)
+ if cn is None:
+ raise ValueError("No such nid %d in the database"%(value))
self.nid=value
else:
raise TypeError("Cannot convert this type to object identifier")
+ def __hash__(self):
+ " Returns NID "
+ return self.nid
def __cmp__(self,other):
+ " Compares NIDs of two objects "
return self.nid-other.nid
def __str__(self):
+ " Default string representation of Oid is dotted-decimal"
return self.dotted()
- def shorttname(self):
+ def __repr__(self):
+ return "Oid('%s')"%(self.dotted())
+ def shortname(self):
+ " Returns short name if any "
return libcrypto.OBJ_nid2sn(self.nid)
def longname(self):
+ " Returns logn name if any "
return libcrypto.OBJ_nid2ln(self.nid)
- def dotted(self)
+ def dotted(self):
+ " Returns dotted-decimal reperesntation "
obj=libcrypto.OBJ_nid2obj(self.nid)
buf=create_string_buffer(256)
libcrypto.OBJ_obj2txt(buf,256,obj,1)
return buf.value
+def create(dotted,shortname,longname):
+ """
+ Creates new OID in the database
+
+ @param dotted - dotted-decimal representation of new OID
+ @param shortname - short name for new OID
+ @param longname - long name for new OID
+
+ @returns Oid object corresponding to new OID
+
+ This function should be used with exreme care. Whenever
+ possible, it is better to add new OIDs via OpenSSL configuration
+ file
+
+ Results of calling this function twice for same OIDor for
+ Oid alredy in database are undefined
+ """
+ nid=libcrypto.OBJ_create(dotted,shortname,longname)
+ if nid == 0:
+ raise LibCryptoError("Problem adding new OID to the database")
+ return Oid(nid)
+
+def cleanup():
+ """
+ Removes all the objects, dynamically added by current
+ application from database.
+ """
+ libcrypto.OBJ_cleanup()
+
+libcrypto.OBJ_nid2sn.restype=c_char_p
+libcrypto.OBJ_nid2ln.restype=c_char_p
+libcrypto.OBJ_nid2obj.restype=c_void_p
+libcrypto.OBJ_obj2txt.argtypes=(c_char_p,c_int,c_void_p,c_int)
+libcrypto.OBJ_txt2nid.argtupes=(c_char_p,)
+libcrypto.OBJ_create.argtypes=(c_char_p,c_char_p,c_char_p)
--- /dev/null
+from distutils.core import setup
+import distutils.cmd
+import sys,os
+
+class MyTests(distutils.cmd.Command):
+ user_options=[]
+ def initialize_options(self):
+ pass
+ def finalize_options(self):
+ pass
+ def run(self):
+ sys.path.insert(0,os.getcwd())
+ import unittest
+ result=unittest.TextTestResult(sys.stdout,True,True)
+ suite= unittest.defaultTestLoader.discover("./tests")
+ print "Discovered %d test cases"%suite.countTestCases()
+ result.buffer=True
+ suite.run(result)
+ print ""
+ if not result.wasSuccessful():
+ if len(result.errors):
+ print "============ Errors disovered ================="
+ for r in result.errors:
+ print r[0],":",r[1]
+
+ if len(result.failures):
+ print "============ Failures disovered ================="
+ for r in result.failures:
+ print r[0],":",r[1]
+ sys.exit(1)
+ else:
+ print "All tests successful"
+
+setup(
+ name="ctypescrypto"
+ version="0.2.0"
+ description="CTypes-based interface for some OpenSSL libcrypto features"
+ author="Victor Wagner",
+ author_email="vitus@wagner.pp.ru",
+ url="https://github.com/vbwagner/ctypescrypto",
+ packages=["ctypescrypto"],
+ cmdlass={"test":MyTests}
+)
+
--- /dev/null
+from ctypescrypto.oid import Oid,create,cleanup
+import unittest
+
+class TestStandard(unittest.TestCase):
+ def test_cn(self):
+ o=Oid("2.5.4.3")
+ self.assertEqual(repr(o),"Oid('2.5.4.3')")
+ self.assertEqual(o.dotted(),"2.5.4.3")
+ self.assertEqual(str(o),"2.5.4.3")
+ self.assertEqual(o.shortname(),"CN")
+ self.assertEqual(o.longname(),"commonName")
+ def test_getnid(self):
+ o=Oid("2.5.4.3")
+ x=Oid("CN")
+ self.assertEqual(o.nid,x.nid)
+ self.assertEqual(o,x)
+ self.assertEqual(hash(o),hash(x))
+
+ def test_cons2(self):
+ o=Oid("2.5.4.3")
+ x=Oid("commonName")
+ self.assertEqual(o.nid,x.nid)
+ def test_bynid(self):
+ o=Oid("2.5.4.3")
+ x=Oid(o.nid)
+ self.assertEqual(o.nid,x.nid)
+ def test_wrongoid(self):
+ with self.assertRaises(ValueError):
+ o=Oid("1.2.3.4.5.6.7.8.10.111.1111")
+ def test_wrongname(self):
+ with self.assertRaises(ValueError):
+ o=Oid("No such oid in the database")
+ def test_wrongtype(self):
+ with self.assertRaises(TypeError):
+ o=Oid([2,5,3,4])
+
+class TestCustom(unittest.TestCase):
+ def testCreate(self):
+ d='1.2.643.100.3'
+ sn="SNILS"
+ long_name="Russian Pension security number"
+ o=create(d,sn,long_name)
+ self.assertEqual(str(o),d)
+ self.assertEqual(o.shortname(),sn)
+ self.assertEqual(o.longname(),long_name)
+ def testLookup(self):
+ d='1.2.643.100.3'
+ sn="SNILS"
+ long_name="Russian Pension security number"
+ o=create(d,sn,long_name)
+ x=Oid(sn)
+ self.assertEqual(o,x)
+ def testCleanup(self):
+ d='1.2.643.100.3'
+ sn="SNILS"
+ long_name="Russian Pension security number"
+ o=create(d,sn,long_name)
+ cleanup()
+ with self.assertRaises(ValueError):
+ x=Oid(sn)
+ def tearDown(self):
+ # Always call cleanup before next test
+ cleanup()
+
+
+
+
+if __name__ == '__main__':
+ unittest.main()