using Invercargill; namespace Riddle { public class DecentralisedNameInfo : NameInfo { public uint8[] public_key { get; protected set; } private uint8[] raw_data { get; protected set; } public override string get_encoded() { return Base64.encode(raw_data); } public DecentralisedNameInfo.from_string(string encoded_name_info) throws NameInfoError { raw_data = Base64.decode (encoded_name_info); public_key = raw_data[0:Sodium.Asymmetric.Signing.PUBLIC_KEY_BYTES]; var signed_message = raw_data[Sodium.Asymmetric.Signing.PUBLIC_KEY_BYTES:]; var data = Sodium.Asymmetric.Signing.verify (signed_message, public_key); if(data == null) { throw new NameInfoError.BAD_DATA("Invalid signature"); } parse_base_info ((string)data); validate_base_info(); if(!name.has_suffix (".rns")) { throw new NameInfoError.INVALID("Decentralised names must end in '.rns'."); } var expected_suffix = generate_suffix(get_root_name(name), public_key); if(!name.has_suffix (expected_suffix)) { throw new NameInfoError.INVALID("The name and private key combination is not valid."); } } public DecentralisedNameInfo(string name, string key, DateTime expiry, Enumerable properties) { var keys = key.split(":"); public_key = Base64.decode(keys[0]); var secret_key = Base64.decode(keys[1]); var name_parts = name.split("."); var root_name = name_parts[name_parts.length-1]; this.name = name + generate_suffix(root_name, public_key); expires = expiry.to_utc(); effective = new DateTime.now_utc(); this.properties = properties.to_sequence(); var data = stringify_base_info(); var signed = Sodium.Asymmetric.Signing.sign (data.data, secret_key); // Data is public-key followed by signed data raw_data = new uint8[public_key.length + signed.length]; for(var i = 0; i < raw_data.length; i++) { if(i < public_key.length) { raw_data[i] = public_key[i]; } else { raw_data[i] = signed[i-public_key.length]; } } } public static string generate_key() { var pk = new uint8[Sodium.Asymmetric.Signing.PUBLIC_KEY_BYTES]; var sk = new uint8[Sodium.Asymmetric.Signing.SECRET_KEY_BYTES]; Sodium.Asymmetric.Signing.generate_keypair (pk, sk); var pk_enc = Base64.encode (pk); var sk_enc = Base64.encode (sk); return @"$pk_enc:$sk_enc"; } public static string generate_suffix(string root_name, uint8[] public_key) { var checksum = new Checksum(ChecksumType.SHA512); var name_data = root_name.data; checksum.update(name_data, name_data.length); checksum.update(public_key, public_key.length); var digest = new uint8[64]; size_t size = digest.length; checksum.get_digest(digest, ref size); var suffix = @".$(encode_hash(digest)).rns"; return suffix; } public static string get_root_name(string name) { var parts = name.split("."); return parts[parts.length - 3]; } private const string ENCODING_CHARS = "abcdefghijklmnopqrstuvwxyz234567"; private static string encode_hash(uint8[] data) { var s = new char[6]; for(var i = 0; i < 6; i++) { var v = data[i*10]; var ev = v / 8; s[i] = ENCODING_CHARS[ev]; } return (string)s; } } }