Created
October 20, 2022 17:56
Revisions
-
DanaEpp created this gist
Oct 20, 2022 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,109 @@ #!/bin/env python3 import argparse import datetime import re import sys import uuid ############################################################################### # Based off of Daniel Thatcher's guid tool # Additional reading from https://duo.com/labs/tech-notes/breaking-down-uuids ############################################################################### # A nano second is a billionth of a second, so... # 1 second = 1e7 100-nanosecond intervals NANO_INTERVAL = 1e7 def uuid_time(uid): # Gregorian reform to the Christian calendar (Oct 15, 1582) # See https://datatracker.ietf.org/doc/html/rfc4122#section-4.2.2 dt_base = datetime.datetime( 1582, 10, 15 ) return dt_base + datetime.timedelta(microseconds=uid.time//10) def uuid_mac(uid): return ":".join(re.findall('..', '%012x' % uid.node)) def dump_guid(guid): try: uid = uuid.UUID(guid) except ValueError: print("Invalid GUID") sys.exit(2) print ("GUID version: {}".format(uid.version)) if uid.version == 1: t = uuid_time(uid) print("Time: {}".format(t)) print("Timestamp: {}".format(uid.time)) print("Node: {}".format(uid.node)) m = uuid_mac(uid) print("MAC address: {}".format(m)) print("Clock sequence: {}".format(uid.clock_seq)) def uuid1(node, clock_seq, timestamp): time_low = timestamp & 0xffffffff time_mid = (timestamp >> 32) & 0xffff time_hi_version = (timestamp >> 48) & 0x0fff clock_seq_low = clock_seq & 0xff clock_seq_hi_variant = (clock_seq >> 8) & 0x3f return uuid.UUID(fields=(time_low, time_mid, time_hi_version, clock_seq_hi_variant, clock_seq_low, node), version=1) def get_precision(timestamp): # Determine the precision by looking at how many 0 are at the end # of the previously captured timestamp ts = str(timestamp) l = len(ts) - len(ts.rstrip('0')) return int("1".ljust(l+1, '0')) def gen_guids(sample_guid, estimated_ts): uid = uuid.UUID(sample_guid) if uid.version != 1: print( "We can only generate GUIDs v1. Aborting." ) sys.exit(2) # Calculate the timestamp for the first GUID dt_base = datetime.datetime( 1582, 10, 15 ) base_guid_time = estimated_ts - dt_base base_timestamp = int(base_guid_time.total_seconds() * NANO_INTERVAL) seconds = 2 precision = get_precision(uid.time) start_time = int(base_timestamp - (NANO_INTERVAL) * seconds) end_time = int(base_timestamp + (NANO_INTERVAL) * seconds) for t in range( start_time, end_time, precision ): yield uuid1(uid.node, uid.clock_seq, t) def main(): parser = argparse.ArgumentParser() parser.add_argument("-d", "--dump", help="Dump decoded GUID and exit", action="store_true") parser.add_argument("-t", "--time", help="The estimated time at which the GUID was generated. ie: '2021-02-27 17:42:01'", type=lambda s: datetime.datetime.strptime(s, "%Y-%m-%d %H:%M:%S")) parser.add_argument("guid", help="The GUID to inspect") args = parser.parse_args() # Validate GUID try: _ = uuid.UUID(args.guid) except: print("Invalid GUID. Aborting.") sys.exit(1) # Dump GUID and exit if that's what we want if args.dump: dump_guid(args.guid) sys.exit(0) if args.time is None: print( "Timestamp required. Use '-t' option. Aborting.") sys.exit(1) for u in gen_guids(args.guid, args.time): print(u) if __name__ == "__main__": main()