using Invercargill; using Invercargill.Mapping; using Invercargill.DataStructures; using InvercargillJson; namespace Spry { public class CryptographyProvider : Object { private uint8[] signing_secret_key; private uint8[] signing_public_key; private uint8[] sealing_secret_key; private uint8[] sealing_public_key; construct { signing_secret_key = new uint8[Sodium.Asymmetric.Signing.SECRET_KEY_BYTES]; signing_public_key = new uint8[Sodium.Asymmetric.Signing.PUBLIC_KEY_BYTES]; Sodium.Asymmetric.Signing.generate_keypair(signing_public_key, signing_secret_key); sealing_secret_key = new uint8[Sodium.Asymmetric.Sealing.SECRET_KEY_BYTES]; sealing_public_key = new uint8[Sodium.Asymmetric.Sealing.PUBLIC_KEY_BYTES]; Sodium.Asymmetric.Sealing.generate_keypair(sealing_public_key, sealing_secret_key); } public string author_component_context_blob(ComponentContext context) throws Error { var mapper = ComponentContext.get_mapper(); var properties = mapper.map_from(context); var json = new JsonElement.from_properties(properties); var blob = json.stringify(false); var signed = Sodium.Asymmetric.Signing.sign(blob.data, signing_secret_key); var @sealed = Sodium.Asymmetric.Sealing.seal(signed, sealing_public_key); return Base64.encode(@sealed).replace("+", "-").replace("/", "_"); } public ComponentContext read_component_context_blob(string blob) throws Error { var mapper = ComponentContext.get_mapper(); var signed = Sodium.Asymmetric.Sealing.unseal(Base64.decode(blob.replace("-", "+").replace("_", "/")), sealing_public_key, sealing_secret_key); if(signed == null) { throw new ComponentError.INVALID_CONTEXT("Could not unseal context"); } var cleartext = Sodium.Asymmetric.Signing.verify(signed, signing_public_key); if(signed == null) { throw new ComponentError.INVALID_CONTEXT("Invalid context signature"); } var json = new JsonElement.from_string(Wrap.byte_array(cleartext).to_raw_string()); var context = mapper.materialise(json.as()); return context; } } public class ComponentContext { public string type_name { get; set; } public string instance_id { get; set; } public DateTime timestamp { get; set; } public Properties data { get; set; } public static PropertyMapper get_mapper() { return PropertyMapper.build_for(cfg => { cfg.map("c", o => o.type_name, (o, v) => o.type_name = v); cfg.map("i", o => o.instance_id, (o, v) => o.instance_id = v); cfg.map("t", o => o.timestamp.format_iso8601(), (o, v) => o.timestamp = new DateTime.from_iso8601(v, new TimeZone.utc())); cfg.map("d", o => o.data, (o, v) => o.data = v); cfg.set_constructor(() => new ComponentContext()); }); } } }