summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/mozpkix/tools/DottedOIDToCode.py
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/mozpkix/tools/DottedOIDToCode.py')
-rw-r--r--security/nss/lib/mozpkix/tools/DottedOIDToCode.py216
1 files changed, 216 insertions, 0 deletions
diff --git a/security/nss/lib/mozpkix/tools/DottedOIDToCode.py b/security/nss/lib/mozpkix/tools/DottedOIDToCode.py
new file mode 100644
index 000000000..dfd4ade07
--- /dev/null
+++ b/security/nss/lib/mozpkix/tools/DottedOIDToCode.py
@@ -0,0 +1,216 @@
+# This code is made available to you under your choice of the following sets
+# of licensing terms:
+###############################################################################
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+###############################################################################
+# Copyright 2013 Mozilla Contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import print_function
+import argparse
+import itertools
+import sys
+
+
+def base128(value):
+ """
+ Given an integral value, returns an array of the base-128 representation
+ of that value, with all but the last byte having the high bit set as
+ required by the DER rules for the nodes of an OID after the first two
+ bytes.
+
+ >>> base128(1)
+ [1]
+ >>> base128(10045)
+ [206, 61]
+ """
+
+ if value < 0:
+ raise ValueError("An OID must have only positive-value nodes.")
+
+ # least significant byte has highest bit unset
+ result = [value % 0x80]
+ value /= 0x80
+
+ while value != 0:
+ result = [0x80 | (value % 0x80)] + result
+ value /= 0x80
+
+ return result
+
+
+def dottedOIDToEncodedArray(dottedOID):
+ """
+ Takes a dotted OID string (e.g. '1.2.840.10045.4.3.4') as input, and
+ returns an array that contains the DER encoding of its value, without
+ the tag and length (e.g. [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04]).
+ """
+ nodes = [int(x) for x in dottedOID.strip().split(".")]
+ if len(nodes) < 2:
+ raise ValueError("An OID must have at least two nodes.")
+ if not (0 <= nodes[0] <= 2):
+ raise ValueError("The first node of an OID must be 0, 1, or 2.")
+ if not (0 <= nodes[1] <= 39):
+ # XXX: Does this restriction apply when the first part is 2?
+ raise ValueError("The second node of an OID must be 0-39.")
+ firstByte = (40 * nodes[0]) + nodes[1]
+ restBase128 = [base128(x) for x in nodes[2:]]
+ return [firstByte] + list(itertools.chain.from_iterable(restBase128))
+
+
+def dottedOIDToCArray(dottedOID, mode):
+ """
+ Takes a dotted OID string (e.g. '1.2.840.10045.4.3.4') as input, and
+ returns a string that contains the hex encoding of the OID in C++ literal
+ notation, e.g. '0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04'.
+ """
+ bytes = dottedOIDToEncodedArray(dottedOID)
+
+ if mode != "value" and mode != "prefixdefine":
+ bytes = [0x06, len(bytes)] + bytes
+
+ if mode == "alg":
+ # Wrap the DER-encoded OID in a SEQUENCE to create an
+ # AlgorithmIdentifier with no parameters.
+ bytes = [0x30, len(bytes)] + bytes
+
+ return ", ".join(["0x%.2x" % b for b in bytes])
+
+
+def specNameToCName(specName):
+ """
+ Given an string containing an ASN.1 name, returns a string that is a valid
+ C++ identifier that is as similar to that name as possible. Since most
+ ASN.1 identifiers used in PKIX specifications are legal C++ names except
+ for containing hyphens, this function just converts the hyphens to
+ underscores. This may need to be improved in the future if we encounter
+ names with other funny characters.
+ """
+ return specName.replace("-", "_")
+
+
+def toCode(programName, specName, dottedOID, mode):
+ """
+ Given an ASN.1 name and a string containing the dotted representation of an
+ OID, returns a string that contains a C++ declaration for a named constant
+ that contains that OID value. If mode is "value" then only the value of
+ the OID (without the tag or length) will be included in the output. If mode
+ is "tlv" then the value will be prefixed with the tag and length. If mode
+ is "alg" then the value will be a complete der-encoded AlgorithmIdentifier
+ with no parameters.
+
+ This:
+
+ toCode("DottedOIDToCode.py", "ecdsa-with-SHA512", "1.2.840.10045.4.3.4",
+ "value")
+
+ would result in a string like:
+
+ // python DottedOIDToCode.py ecdsa-with-SHA512 1.2.840.10045.4.3.4
+ static const uint8_t ecdsa_with_SHA512[] = {
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04
+ };
+
+ This:
+
+ toCode("DottedOIDToCode.py", "ecdsa-with-SHA512", "1.2.840.10045.4.3.4",
+ "tlv")
+
+ would result in a string like:
+
+ // python DottedOIDToCode.py --tlv ecdsa-with-SHA512 1.2.840.10045.4.3.4
+ static const uint8_t tlv_ecdsa_with_SHA512[] = {
+ 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04
+ };
+
+ This:
+
+ toCode("DottedOIDToCode.py", "ecdsa-with-SHA512", "1.2.840.10045.4.3.4",
+ "alg")
+
+ would result in a string like:
+
+ // python DottedOIDToCode.py --alg ecdsa-with-SHA512 1.2.840.10045.4.3.4
+ static const uint8_t alg_ecdsa_with_SHA512[] = {
+ 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04
+ };
+
+ This:
+
+ toCode("DottedOIDToCode.py", "PREFIX_1_2_840_10045", "1.2.840.10045",
+ "prefixdefine")
+
+ would result in a string like this (note the lack of indention):
+
+ // python DottedOIDToCode.py --prefixdefine PREFIX_1_2_840_10045 1.2.840.10045
+ #define PREFIX_1_2_840_10045 0x2a, 0x86, 0x48, 0xce, 0x3d
+ """
+ programNameWithOptions = programName
+
+ if mode == "prefixdefine":
+ programNameWithOptions += " --prefixdefine"
+ varName = specName
+ return ("// python %s %s %s\n" +
+ "#define %s %s\n") % (programNameWithOptions, specName,
+ dottedOID, varName,
+ dottedOIDToCArray(dottedOID, mode))
+
+ varName = specNameToCName(specName)
+ if mode == "tlv":
+ programNameWithOptions += " --tlv"
+ varName = "tlv_" + varName
+ elif mode == "alg":
+ programNameWithOptions += " --alg"
+ varName = "alg_" + varName
+ elif mode == "prefixdefine":
+ programNameWithOptions += " --alg"
+ varName = varName
+
+ return (" // python %s %s %s\n" +
+ " static const uint8_t %s[] = {\n" +
+ " %s\n" +
+ " };\n") % (programNameWithOptions, specName, dottedOID, varName,
+ dottedOIDToCArray(dottedOID, mode))
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(
+ description="Generate code snippets to handle OIDs in C++",
+ epilog="example: python %s ecdsa-with-SHA1 1.2.840.10045.4.1"
+ % sys.argv[0])
+ group = parser.add_mutually_exclusive_group()
+ group.add_argument("--tlv", action='store_true',
+ help="Wrap the encoded OID value with the tag and length")
+ group.add_argument("--alg", action='store_true',
+ help="Wrap the encoded OID value in an encoded SignatureAlgorithm")
+ group.add_argument("--prefixdefine", action='store_true',
+ help="generate a OID prefix #define")
+ parser.add_argument("name",
+ help="The name given to the OID in the specification")
+ parser.add_argument("dottedOID", metavar="dotted-oid",
+ help="The OID value, in dotted notation")
+
+ args = parser.parse_args()
+ if args.alg:
+ mode = 'alg'
+ elif args.tlv:
+ mode = 'tlv'
+ elif args.prefixdefine:
+ mode = 'prefixdefine'
+ else:
+ mode = 'value'
+
+ print(toCode(sys.argv[0], args.name, args.dottedOID, mode))