aboutsummaryrefslogtreecommitdiff
path: root/xmpp-vala/src/module/xep/0300_cryptographic_hashes.vala
diff options
context:
space:
mode:
Diffstat (limited to 'xmpp-vala/src/module/xep/0300_cryptographic_hashes.vala')
-rw-r--r--xmpp-vala/src/module/xep/0300_cryptographic_hashes.vala136
1 files changed, 136 insertions, 0 deletions
diff --git a/xmpp-vala/src/module/xep/0300_cryptographic_hashes.vala b/xmpp-vala/src/module/xep/0300_cryptographic_hashes.vala
new file mode 100644
index 00000000..00f9e2ee
--- /dev/null
+++ b/xmpp-vala/src/module/xep/0300_cryptographic_hashes.vala
@@ -0,0 +1,136 @@
+using GLib;
+using Gee;
+
+namespace Xmpp.Xep.CryptographicHashes {
+ public const string NS_URI = "urn:xmpp:hashes:2";
+
+ public enum HashCmp {
+ Match,
+ Mismatch,
+ None,
+ }
+
+ public class Hash {
+ public string algo;
+ // hash encoded in Base64
+ public string val;
+
+ public static string hash_name(ChecksumType type) {
+ switch(type) {
+ case ChecksumType.MD5:
+ return "md5";
+ case ChecksumType.SHA1:
+ return "sha-1";
+ case ChecksumType.SHA256:
+ return "sha-256";
+ case ChecksumType.SHA384:
+ return "sha-384";
+ case ChecksumType.SHA512:
+ return "sha-512";
+ }
+ return "(null)";
+ }
+
+ public static ChecksumType? supported_hash(string hash) {
+ switch (hash) {
+ case "sha-1":
+ return ChecksumType.SHA1;
+ case "sha-256":
+ return ChecksumType.SHA256;
+ case "sha-384":
+ return ChecksumType.SHA384;
+ case "sha-512":
+ return ChecksumType.SHA512;
+ }
+ return null;
+ }
+
+ public Hash.from_data(GLib.ChecksumType type, uint8[] data) {
+ GLib.Checksum checksum = new GLib.Checksum(type);
+ checksum.update(data, data.length);
+ // 64 * 8 = 512 (sha-512 is the longest hash variant)
+ uint8[] digest = new uint8[64];
+ size_t length = digest.length;
+ checksum.get_digest(digest, ref length);
+ this.algo = hash_name(type);
+ this.val = GLib.Base64.encode(digest[0:length]);
+ }
+
+ public HashCmp compare(Hash other) {
+ if (this.algo != other.algo) {
+ return HashCmp.None;
+ }
+ if (this.val == other.val) {
+ return HashCmp.Match;
+ } else {
+ return HashCmp.Mismatch;
+ }
+ }
+
+ public StanzaNode to_stanza_node() {
+ return new StanzaNode.build("hash", NS_URI).add_self_xmlns()
+ .put_attribute("algo", this.algo)
+ .put_node(new StanzaNode.text(this.val));
+ }
+
+ public Hash.from_stanza_node(StanzaNode node) {
+ this.algo = node.get_attribute("algo");
+ this.val = node.get_string_content();
+ }
+ }
+
+ public class Hashes {
+ public Gee.List<Hash> hashes = new ArrayList<Hash>();
+
+ public Gee.List<ChecksumType> supported_hashes() {
+ Gee.List<ChecksumType> supported = new ArrayList<ChecksumType>();
+ foreach (Hash hash in this.hashes) {
+ ChecksumType? hash_type = Hash.supported_hash(hash.algo);
+ if (hash_type != null) {
+ supported.add(hash_type);
+ }
+ }
+ return supported;
+ }
+
+ public Hashes.from_data(Gee.List<ChecksumType> types, uint8[] data) {
+ foreach (ChecksumType type in types) {
+ this.hashes.add(new Hash.from_data(type, data));
+ }
+ }
+
+ public HashCmp compare(Hashes other) {
+ HashCmp cmp = HashCmp.None;
+ foreach (Hash this_hash in this.hashes) {
+ foreach (Hash other_hash in other.hashes) {
+ switch (this_hash.compare(other_hash)) {
+ case HashCmp.Mismatch:
+ return HashCmp.Mismatch;
+ case HashCmp.Match:
+ cmp = HashCmp.Match;
+ break;
+ case HashCmp.None:
+ continue;
+ }
+ }
+ }
+ return cmp;
+ }
+
+ public Gee.List<StanzaNode> to_stanza_nodes() {
+ Gee.List<StanzaNode> nodes = new ArrayList<StanzaNode>();
+ foreach (Hash hash in this.hashes) {
+ nodes.add(hash.to_stanza_node());
+ }
+ return nodes;
+ }
+
+ public Hashes.from_stanza_subnodes(StanzaNode node) {
+ Gee.List<StanzaNode> subnodes = node.get_subnodes("hash", NS_URI);
+ this.hashes = new ArrayList<Hash>();
+ foreach (StanzaNode subnode in subnodes) {
+ this.hashes.add(new Hash.from_stanza_node(subnode));
+ }
+ }
+ }
+}