// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using Microsoft.Research.SEAL; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace SEALNetTest { [TestClass] public class GaloisKeysTests { [TestMethod] public void CreateTest() { GaloisKeys keys = new GaloisKeys(); Assert.IsNotNull(keys); Assert.AreEqual(0ul, keys.Size); } [TestMethod] public void CreateNonEmptyTest() { { SEALContext context = GlobalContext.BFVContext; KeyGenerator keygen = new KeyGenerator(context); keygen.CreateGaloisKeys(out GaloisKeys keys); Assert.IsNotNull(keys); Assert.AreEqual(24ul, keys.Size); GaloisKeys copy = new GaloisKeys(keys); Assert.IsNotNull(copy); Assert.AreEqual(24ul, copy.Size); } { SEALContext context = GlobalContext.BGVContext; KeyGenerator keygen = new KeyGenerator(context); keygen.CreateGaloisKeys(out GaloisKeys keys); Assert.IsNotNull(keys); Assert.AreEqual(24ul, keys.Size); GaloisKeys copy = new GaloisKeys(keys); Assert.IsNotNull(copy); Assert.AreEqual(24ul, copy.Size); } } [TestMethod] public void SaveLoadTest() { { SEALContext context = GlobalContext.BFVContext; KeyGenerator keygen = new KeyGenerator(context); keygen.CreateGaloisKeys(out GaloisKeys keys); GaloisKeys other = new GaloisKeys(); Assert.IsNotNull(keys); Assert.AreEqual(24ul, keys.Size); using (MemoryStream ms = new MemoryStream()) { keys.Save(ms); ms.Seek(offset: 0, loc: SeekOrigin.Begin); other.Load(context, ms); } Assert.AreEqual(24ul, other.Size); Assert.IsTrue(ValCheck.IsValidFor(other, context)); List> keysData = new List>(keys.Data); List> otherData = new List>(other.Data); Assert.AreEqual(keysData.Count, otherData.Count); for (int i = 0; i < keysData.Count; i++) { List keysCiphers = new List(keysData[i]); List otherCiphers = new List(otherData[i]); Assert.AreEqual(keysCiphers.Count, otherCiphers.Count); for (int j = 0; j < keysCiphers.Count; j++) { PublicKey keysCipher = keysCiphers[j]; PublicKey otherCipher = otherCiphers[j]; Assert.AreEqual(keysCipher.Data.Size, otherCipher.Data.Size); Assert.AreEqual(keysCipher.Data.PolyModulusDegree, otherCipher.Data.PolyModulusDegree); Assert.AreEqual(keysCipher.Data.CoeffModulusSize, otherCipher.Data.CoeffModulusSize); ulong coeffCount = keysCipher.Data.Size * keysCipher.Data.PolyModulusDegree * keysCipher.Data.CoeffModulusSize; for (ulong k = 0; k < coeffCount; k++) { Assert.AreEqual(keysCipher.Data[k], otherCipher.Data[k]); } } } } { SEALContext context = GlobalContext.BGVContext; KeyGenerator keygen = new KeyGenerator(context); keygen.CreateGaloisKeys(out GaloisKeys keys); GaloisKeys other = new GaloisKeys(); Assert.IsNotNull(keys); Assert.AreEqual(24ul, keys.Size); using (MemoryStream ms = new MemoryStream()) { keys.Save(ms); ms.Seek(offset: 0, loc: SeekOrigin.Begin); other.Load(context, ms); } Assert.AreEqual(24ul, other.Size); Assert.IsTrue(ValCheck.IsValidFor(other, context)); List> keysData = new List>(keys.Data); List> otherData = new List>(other.Data); Assert.AreEqual(keysData.Count, otherData.Count); for (int i = 0; i < keysData.Count; i++) { List keysCiphers = new List(keysData[i]); List otherCiphers = new List(otherData[i]); Assert.AreEqual(keysCiphers.Count, otherCiphers.Count); for (int j = 0; j < keysCiphers.Count; j++) { PublicKey keysCipher = keysCiphers[j]; PublicKey otherCipher = otherCiphers[j]; Assert.AreEqual(keysCipher.Data.Size, otherCipher.Data.Size); Assert.AreEqual(keysCipher.Data.PolyModulusDegree, otherCipher.Data.PolyModulusDegree); Assert.AreEqual(keysCipher.Data.CoeffModulusSize, otherCipher.Data.CoeffModulusSize); ulong coeffCount = keysCipher.Data.Size * keysCipher.Data.PolyModulusDegree * keysCipher.Data.CoeffModulusSize; for (ulong k = 0; k < coeffCount; k++) { Assert.AreEqual(keysCipher.Data[k], otherCipher.Data[k]); } } } } } [TestMethod] public void SeededKeyTest() { { EncryptionParameters parms = new EncryptionParameters(SchemeType.BFV) { PolyModulusDegree = 8, PlainModulus = new Modulus(257), CoeffModulus = CoeffModulus.Create(8, new int[] { 40, 40 }) }; SEALContext context = new SEALContext(parms, expandModChain: false, secLevel: SecLevelType.None); KeyGenerator keygen = new KeyGenerator(context); keygen.CreatePublicKey(out PublicKey publicKey); Encryptor encryptor = new Encryptor(context, publicKey); Decryptor decryptor = new Decryptor(context, keygen.SecretKey); Evaluator evaluator = new Evaluator(context); BatchEncoder encoder = new BatchEncoder(context); GaloisKeys galoisKeys = new GaloisKeys(); using (MemoryStream stream = new MemoryStream()) { keygen.CreateGaloisKeys().Save(stream); stream.Seek(0, SeekOrigin.Begin); galoisKeys.Load(context, stream); } Plaintext plain = new Plaintext(); List vec = new List { 1, 2, 3, 4, 5, 6, 7, 8 }; encoder.Encode(vec, plain); Ciphertext encrypted = new Ciphertext(); Ciphertext encdest = new Ciphertext(); Plaintext plaindest = new Plaintext(); encryptor.Encrypt(plain, encrypted); evaluator.RotateColumns(encrypted, galoisKeys, encdest); decryptor.Decrypt(encdest, plaindest); encoder.Decode(plaindest, vec); Assert.IsTrue(AreCollectionsEqual(vec, new List { 5, 6, 7, 8, 1, 2, 3, 4 })); evaluator.RotateRows(encdest, -1, galoisKeys, encrypted); decryptor.Decrypt(encrypted, plaindest); encoder.Decode(plaindest, vec); Assert.IsTrue(AreCollectionsEqual(vec, new List { 8, 5, 6, 7, 4, 1, 2, 3 })); evaluator.RotateRowsInplace(encrypted, 2, galoisKeys); decryptor.Decrypt(encrypted, plaindest); encoder.Decode(plaindest, vec); Assert.IsTrue(AreCollectionsEqual(vec, new List { 6, 7, 8, 5, 2, 3, 4, 1 })); evaluator.RotateColumnsInplace(encrypted, galoisKeys); decryptor.Decrypt(encrypted, plaindest); encoder.Decode(plaindest, vec); Assert.IsTrue(AreCollectionsEqual(vec, new List { 2, 3, 4, 1, 6, 7, 8, 5 })); } { EncryptionParameters parms = new EncryptionParameters(SchemeType.BGV) { PolyModulusDegree = 8, PlainModulus = new Modulus(257), CoeffModulus = CoeffModulus.Create(8, new int[] { 40, 40 }) }; SEALContext context = new SEALContext(parms, expandModChain: false, secLevel: SecLevelType.None); KeyGenerator keygen = new KeyGenerator(context); keygen.CreatePublicKey(out PublicKey publicKey); Encryptor encryptor = new Encryptor(context, publicKey); Decryptor decryptor = new Decryptor(context, keygen.SecretKey); Evaluator evaluator = new Evaluator(context); BatchEncoder encoder = new BatchEncoder(context); GaloisKeys galoisKeys = new GaloisKeys(); using (MemoryStream stream = new MemoryStream()) { keygen.CreateGaloisKeys().Save(stream); stream.Seek(0, SeekOrigin.Begin); galoisKeys.Load(context, stream); } Plaintext plain = new Plaintext(); List vec = new List { 1, 2, 3, 4, 5, 6, 7, 8 }; encoder.Encode(vec, plain); Ciphertext encrypted = new Ciphertext(); Ciphertext encdest = new Ciphertext(); Plaintext plaindest = new Plaintext(); encryptor.Encrypt(plain, encrypted); evaluator.RotateColumns(encrypted, galoisKeys, encdest); decryptor.Decrypt(encdest, plaindest); encoder.Decode(plaindest, vec); Assert.IsTrue(AreCollectionsEqual(vec, new List { 5, 6, 7, 8, 1, 2, 3, 4 })); evaluator.RotateRows(encdest, -1, galoisKeys, encrypted); decryptor.Decrypt(encrypted, plaindest); encoder.Decode(plaindest, vec); Assert.IsTrue(AreCollectionsEqual(vec, new List { 8, 5, 6, 7, 4, 1, 2, 3 })); evaluator.RotateRowsInplace(encrypted, 2, galoisKeys); decryptor.Decrypt(encrypted, plaindest); encoder.Decode(plaindest, vec); Assert.IsTrue(AreCollectionsEqual(vec, new List { 6, 7, 8, 5, 2, 3, 4, 1 })); evaluator.RotateColumnsInplace(encrypted, galoisKeys); decryptor.Decrypt(encrypted, plaindest); encoder.Decode(plaindest, vec); Assert.IsTrue(AreCollectionsEqual(vec, new List { 2, 3, 4, 1, 6, 7, 8, 5 })); } } [TestMethod] public void SetTest() { { SEALContext context = GlobalContext.BFVContext; KeyGenerator keygen = new KeyGenerator(context); keygen.CreateGaloisKeys(out GaloisKeys keys); Assert.IsNotNull(keys); Assert.AreEqual(24ul, keys.Size); GaloisKeys keys2 = new GaloisKeys(); Assert.IsNotNull(keys2); Assert.AreEqual(0ul, keys2.Size); keys2.Set(keys); Assert.AreNotSame(keys, keys2); Assert.AreEqual(24ul, keys2.Size); } { SEALContext context = GlobalContext.BGVContext; KeyGenerator keygen = new KeyGenerator(context); keygen.CreateGaloisKeys(out GaloisKeys keys); Assert.IsNotNull(keys); Assert.AreEqual(24ul, keys.Size); GaloisKeys keys2 = new GaloisKeys(); Assert.IsNotNull(keys2); Assert.AreEqual(0ul, keys2.Size); keys2.Set(keys); Assert.AreNotSame(keys, keys2); Assert.AreEqual(24ul, keys2.Size); } } [TestMethod] public void KeyTest() { { SEALContext context = GlobalContext.BFVContext; KeyGenerator keygen = new KeyGenerator(context); keygen.CreateGaloisKeys(out GaloisKeys keys); MemoryPoolHandle handle = keys.Pool; Assert.IsNotNull(keys); Assert.AreEqual(24ul, keys.Size); Assert.IsFalse(keys.HasKey(galoisElt: 1)); Assert.IsTrue(keys.HasKey(galoisElt: 3)); Assert.IsFalse(keys.HasKey(galoisElt: 5)); Assert.IsFalse(keys.HasKey(galoisElt: 7)); Assert.IsTrue(keys.HasKey(galoisElt: 9)); Assert.IsFalse(keys.HasKey(galoisElt: 11)); IEnumerable key = keys.Key(3); Assert.AreEqual(4, key.Count()); IEnumerable key2 = keys.Key(9); Assert.AreEqual(4, key2.Count()); Assert.IsTrue(handle.AllocByteCount > 0ul); } { SEALContext context = GlobalContext.BGVContext; KeyGenerator keygen = new KeyGenerator(context); keygen.CreateGaloisKeys(out GaloisKeys keys); MemoryPoolHandle handle = keys.Pool; Assert.IsNotNull(keys); Assert.AreEqual(24ul, keys.Size); Assert.IsFalse(keys.HasKey(galoisElt: 1)); Assert.IsTrue(keys.HasKey(galoisElt: 3)); Assert.IsFalse(keys.HasKey(galoisElt: 5)); Assert.IsFalse(keys.HasKey(galoisElt: 7)); Assert.IsTrue(keys.HasKey(galoisElt: 9)); Assert.IsFalse(keys.HasKey(galoisElt: 11)); IEnumerable key = keys.Key(3); Assert.AreEqual(4, key.Count()); IEnumerable key2 = keys.Key(9); Assert.AreEqual(4, key2.Count()); Assert.IsTrue(handle.AllocByteCount > 0ul); } } [TestMethod] public void KeyEltTest() { { SEALContext context = GlobalContext.BFVContext; KeyGenerator keygen = new KeyGenerator(context); keygen.CreateGaloisKeys(galoisElts: new uint[] { 1, 3 }, out GaloisKeys keys); Assert.IsNotNull(keys); Assert.AreEqual(2ul, keys.Size); Assert.IsTrue(keys.HasKey(1)); Assert.IsTrue(keys.HasKey(3)); Assert.IsFalse(keys.HasKey(5)); } { SEALContext context = GlobalContext.BGVContext; KeyGenerator keygen = new KeyGenerator(context); keygen.CreateGaloisKeys(galoisElts: new uint[] { 1, 3 }, out GaloisKeys keys); Assert.IsNotNull(keys); Assert.AreEqual(2ul, keys.Size); Assert.IsTrue(keys.HasKey(1)); Assert.IsTrue(keys.HasKey(3)); Assert.IsFalse(keys.HasKey(5)); } } [TestMethod] public void KeyStepTest() { EncryptionParameters parms = new EncryptionParameters(SchemeType.CKKS) { PolyModulusDegree = 64, CoeffModulus = CoeffModulus.Create(64, new int[] { 60, 60 }) }; SEALContext context = new SEALContext(parms, expandModChain: false, secLevel: SecLevelType.None); KeyGenerator keygen = new KeyGenerator(context); keygen.CreateGaloisKeys(steps: new int[] { 1, 2, 3 }, out GaloisKeys keys); Assert.IsNotNull(keys); Assert.AreEqual(3ul, keys.Size); Assert.IsFalse(keys.HasKey(1)); Assert.IsTrue(keys.HasKey(3)); Assert.IsFalse(keys.HasKey(5)); Assert.IsFalse(keys.HasKey(7)); Assert.IsTrue(keys.HasKey(9)); Assert.IsFalse(keys.HasKey(11)); Assert.IsFalse(keys.HasKey(13)); Assert.IsFalse(keys.HasKey(15)); Assert.IsFalse(keys.HasKey(17)); Assert.IsFalse(keys.HasKey(19)); Assert.IsFalse(keys.HasKey(21)); Assert.IsFalse(keys.HasKey(23)); Assert.IsFalse(keys.HasKey(25)); Assert.IsTrue(keys.HasKey(27)); } [TestMethod] public void ExceptionsTest() { { SEALContext context = GlobalContext.BFVContext; GaloisKeys keys = new GaloisKeys(); Utilities.AssertThrows(() => keys = new GaloisKeys(null)); Utilities.AssertThrows(() => keys.Set(null)); Utilities.AssertThrows(() => ValCheck.IsValidFor(keys, null)); Utilities.AssertThrows(() => keys.Save(null)); Utilities.AssertThrows(() => keys.UnsafeLoad(context, null)); Utilities.AssertThrows(() => keys.UnsafeLoad(context, new MemoryStream())); Utilities.AssertThrows(() => keys.UnsafeLoad(null, new MemoryStream())); Utilities.AssertThrows(() => keys.Load(context, null)); Utilities.AssertThrows(() => keys.Load(null, new MemoryStream())); } { SEALContext context = GlobalContext.BGVContext; GaloisKeys keys = new GaloisKeys(); Utilities.AssertThrows(() => keys = new GaloisKeys(null)); Utilities.AssertThrows(() => keys.Set(null)); Utilities.AssertThrows(() => ValCheck.IsValidFor(keys, null)); Utilities.AssertThrows(() => keys.Save(null)); Utilities.AssertThrows(() => keys.UnsafeLoad(context, null)); Utilities.AssertThrows(() => keys.UnsafeLoad(context, new MemoryStream())); Utilities.AssertThrows(() => keys.UnsafeLoad(null, new MemoryStream())); Utilities.AssertThrows(() => keys.Load(context, null)); Utilities.AssertThrows(() => keys.Load(null, new MemoryStream())); } } /// /// Returns true if the two given collections have equivalent elements, false otherwise /// private static bool AreCollectionsEqual(IEnumerable coll1, IEnumerable coll2) { int size1 = coll1.Count(); int size2 = coll2.Count(); if (size1 != size2) return false; IEnumerator en1 = coll1.GetEnumerator(); IEnumerator en2 = coll2.GetEnumerator(); while (en1.MoveNext() && en2.MoveNext()) { if (!en1.Current.Equals(en2.Current)) return false; } return true; } } }