Esempio n. 1
0
		public override byte[] Transform(byte[] pbMsg, KdfParameters p)
		{
			if(pbMsg == null) throw new ArgumentNullException("pbMsg");
			if(p == null) throw new ArgumentNullException("p");

			Type tRounds = p.GetTypeOf(ParamRounds);
			if(tRounds == null) throw new ArgumentNullException("p.Rounds");
			if(tRounds != typeof(ulong)) throw new ArgumentOutOfRangeException("p.Rounds");
			ulong uRounds = p.GetUInt64(ParamRounds, 0);

			byte[] pbSeed = p.GetByteArray(ParamSeed);
			if(pbSeed == null) throw new ArgumentNullException("p.Seed");

			if(pbMsg.Length != 32)
			{
				Debug.Assert(false);
				pbMsg = CryptoUtil.HashSha256(pbMsg);
			}

			if(pbSeed.Length != 32)
			{
				Debug.Assert(false);
				pbSeed = CryptoUtil.HashSha256(pbSeed);
			}

			return TransformKey(pbMsg, pbSeed, uRounds);
		}
Esempio n. 2
0
		public override void Randomize(KdfParameters p)
		{
			if(p == null) { Debug.Assert(false); return; }
			Debug.Assert(g_uuid.Equals(p.KdfUuid));

			byte[] pbSeed = CryptoRandom.Instance.GetRandomBytes(32);
			p.SetByteArray(ParamSeed, pbSeed);
		}
        private byte[] GenerateHeader()
        {
            byte[] pbHeader;
            using (MemoryStream ms = new MemoryStream())
            {
                MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileSignature1));
                MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileSignature2));
                MemUtil.Write(ms, MemUtil.UInt32ToBytes(m_uFileVersion));

                WriteHeaderField(ms, KdbxHeaderFieldID.CipherID,
                                 m_pwDatabase.DataCipherUuid.UuidBytes);

                int nCprID = (int)m_pwDatabase.Compression;
                WriteHeaderField(ms, KdbxHeaderFieldID.CompressionFlags,
                                 MemUtil.UInt32ToBytes((uint)nCprID));

                WriteHeaderField(ms, KdbxHeaderFieldID.MasterSeed, m_pbMasterSeed);

                if (m_uFileVersion < FileVersion32_4)
                {
                    Debug.Assert(m_pwDatabase.KdfParameters.KdfUuid.Equals(
                                     (new AesKdf()).Uuid));
                    WriteHeaderField(ms, KdbxHeaderFieldID.TransformSeed,
                                     m_pwDatabase.KdfParameters.GetByteArray(AesKdf.ParamSeed));
                    WriteHeaderField(ms, KdbxHeaderFieldID.TransformRounds,
                                     MemUtil.UInt64ToBytes(m_pwDatabase.KdfParameters.GetUInt64(
                                                               AesKdf.ParamRounds, PwDefs.DefaultKeyEncryptionRounds)));
                }
                else
                {
                    WriteHeaderField(ms, KdbxHeaderFieldID.KdfParameters,
                                     KdfParameters.SerializeExt(m_pwDatabase.KdfParameters));
                }

                if (m_pbEncryptionIV.Length > 0)
                {
                    WriteHeaderField(ms, KdbxHeaderFieldID.EncryptionIV, m_pbEncryptionIV);
                }

                if (m_uFileVersion < FileVersion32_4)
                {
                    WriteHeaderField(ms, KdbxHeaderFieldID.InnerRandomStreamKey,
                                     m_pbInnerRandomStreamKey);

                    WriteHeaderField(ms, KdbxHeaderFieldID.StreamStartBytes,
                                     m_pbStreamStartBytes);

                    int nIrsID = (int)m_craInnerRandomStream;
                    WriteHeaderField(ms, KdbxHeaderFieldID.InnerRandomStreamID,
                                     MemUtil.Int32ToBytes(nIrsID));
                }

                // Write public custom data only when there is at least one item,
                // because KDBX 3.1 didn't support this field yet
                if (m_pwDatabase.PublicCustomData.Count > 0)
                {
                    WriteHeaderField(ms, KdbxHeaderFieldID.PublicCustomData,
                                     VariantDictionary.Serialize(m_pwDatabase.PublicCustomData));
                }

                WriteHeaderField(ms, KdbxHeaderFieldID.EndOfHeader, new byte[] {
                    (byte)'\r', (byte)'\n', (byte)'\r', (byte)'\n'
                });

                pbHeader = ms.ToArray();
            }

            return(pbHeader);
        }
        private bool ReadHeaderField(BinaryReaderEx brSource)
        {
            Debug.Assert(brSource != null);
            if (brSource == null)
            {
                throw new ArgumentNullException("brSource");
            }

            byte btFieldID = brSource.ReadByte();

            int cbSize;

            Debug.Assert(m_uFileVersion > 0);
            if (m_uFileVersion < FileVersion32_4)
            {
                cbSize = (int)MemUtil.BytesToUInt16(brSource.ReadBytes(2));
            }
            else
            {
                cbSize = MemUtil.BytesToInt32(brSource.ReadBytes(4));
            }
            if (cbSize < 0)
            {
                throw new FormatException(KLRes.FileCorrupted);
            }

            byte[] pbData = MemUtil.EmptyByteArray;
            if (cbSize > 0)
            {
                pbData = brSource.ReadBytes(cbSize);
            }

            bool bResult            = true;
            KdbxHeaderFieldID kdbID = (KdbxHeaderFieldID)btFieldID;

            switch (kdbID)
            {
            case KdbxHeaderFieldID.EndOfHeader:
                bResult = false;                         // Returning false indicates end of header
                break;

            case KdbxHeaderFieldID.CipherID:
                SetCipher(pbData);
                break;

            case KdbxHeaderFieldID.CompressionFlags:
                SetCompressionFlags(pbData);
                break;

            case KdbxHeaderFieldID.MasterSeed:
                m_pbMasterSeed = pbData;
                CryptoRandom.Instance.AddEntropy(pbData);
                break;

            // Obsolete; for backward compatibility only
            case KdbxHeaderFieldID.TransformSeed:
                Debug.Assert(m_uFileVersion < FileVersion32_4);

                AesKdf kdfS = new AesKdf();
                if (!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfS.Uuid))
                {
                    m_pwDatabase.KdfParameters = kdfS.GetDefaultParameters();
                }

                // m_pbTransformSeed = pbData;
                m_pwDatabase.KdfParameters.SetByteArray(AesKdf.ParamSeed, pbData);

                CryptoRandom.Instance.AddEntropy(pbData);
                break;

            // Obsolete; for backward compatibility only
            case KdbxHeaderFieldID.TransformRounds:
                Debug.Assert(m_uFileVersion < FileVersion32_4);

                AesKdf kdfR = new AesKdf();
                if (!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfR.Uuid))
                {
                    m_pwDatabase.KdfParameters = kdfR.GetDefaultParameters();
                }

                // m_pwDatabase.KeyEncryptionRounds = MemUtil.BytesToUInt64(pbData);
                m_pwDatabase.KdfParameters.SetUInt64(AesKdf.ParamRounds,
                                                     MemUtil.BytesToUInt64(pbData));
                break;

            case KdbxHeaderFieldID.EncryptionIV:
                m_pbEncryptionIV = pbData;
                break;

            case KdbxHeaderFieldID.InnerRandomStreamKey:
                Debug.Assert(m_uFileVersion < FileVersion32_4);
                Debug.Assert(m_pbInnerRandomStreamKey == null);
                m_pbInnerRandomStreamKey = pbData;
                CryptoRandom.Instance.AddEntropy(pbData);
                break;

            case KdbxHeaderFieldID.StreamStartBytes:
                Debug.Assert(m_uFileVersion < FileVersion32_4);
                m_pbStreamStartBytes = pbData;
                break;

            case KdbxHeaderFieldID.InnerRandomStreamID:
                Debug.Assert(m_uFileVersion < FileVersion32_4);
                SetInnerRandomStreamID(pbData);
                break;

            case KdbxHeaderFieldID.KdfParameters:
                m_pwDatabase.KdfParameters = KdfParameters.DeserializeExt(pbData);
                break;

            case KdbxHeaderFieldID.PublicCustomData:
                Debug.Assert(m_pwDatabase.PublicCustomData.Count == 0);
                m_pwDatabase.PublicCustomData = VariantDictionary.Deserialize(pbData);
                break;

            default:
                Debug.Assert(false);
                if (m_slLogger != null)
                {
                    m_slLogger.SetText(KLRes.UnknownHeaderId + @": " +
                                       kdbID.ToString() + "!", LogStatusType.Warning);
                }
                break;
            }

            return(bResult);
        }
Esempio n. 5
0
        public override bool Export(PwExportInfo pwExportInfo, Stream sOutput,
                                    IStatusLogger slLogger)
        {
            PwDatabase pd     = pwExportInfo.ContextDatabase;
            PwGroup    pgRoot = pwExportInfo.DataGroup;

            // Remove everything that requires KDBX 4 or higher;
            // see also KdbxFile.GetMinKdbxVersion

            KdfParameters pKdf   = pd.KdfParameters;
            AesKdf        kdfAes = new AesKdf();

            if (!kdfAes.Uuid.Equals(pKdf.KdfUuid))
            {
                pd.KdfParameters = kdfAes.GetDefaultParameters();
            }

            VariantDictionary vdPublic = pd.PublicCustomData;

            pd.PublicCustomData = new VariantDictionary();

            List <PwGroup>            lCustomGK = new List <PwGroup>();
            List <StringDictionaryEx> lCustomGV = new List <StringDictionaryEx>();
            List <PwEntry>            lCustomEK = new List <PwEntry>();
            List <StringDictionaryEx> lCustomEV = new List <StringDictionaryEx>();

            GroupHandler gh = delegate(PwGroup pg)
            {
                if (pg == null)
                {
                    Debug.Assert(false); return(true);
                }
                if (pg.CustomData.Count > 0)
                {
                    lCustomGK.Add(pg);
                    lCustomGV.Add(pg.CustomData);
                    pg.CustomData = new StringDictionaryEx();
                }
                return(true);
            };
            EntryHandler eh = delegate(PwEntry pe)
            {
                if (pe == null)
                {
                    Debug.Assert(false); return(true);
                }
                if (pe.CustomData.Count > 0)
                {
                    lCustomEK.Add(pe);
                    lCustomEV.Add(pe.CustomData);
                    pe.CustomData = new StringDictionaryEx();
                }
                return(true);
            };

            gh(pgRoot);
            pgRoot.TraverseTree(TraversalMethod.PreOrder, gh, eh);

            try
            {
                KdbxFile kdbx = new KdbxFile(pd);
                kdbx.ForceVersion = KdbxFile.FileVersion32_3;
                kdbx.Save(sOutput, pgRoot, KdbxFormat.Default, slLogger);
            }
            finally
            {
                // Restore

                pd.KdfParameters    = pKdf;
                pd.PublicCustomData = vdPublic;

                for (int i = 0; i < lCustomGK.Count; ++i)
                {
                    lCustomGK[i].CustomData = lCustomGV[i];
                }
                for (int i = 0; i < lCustomEK.Count; ++i)
                {
                    lCustomEK[i].CustomData = lCustomEV[i];
                }
            }

            return(true);
        }
Esempio n. 6
0
        private KdfParameters GetKdfParameters(bool bShowAdjustments)
        {
            KdfEngine kdf = GetKdf();

            if (kdf == null)
            {
                Debug.Assert(false); return(null);
            }

            string strAdj = string.Empty;

            KdfParameters pKdf = kdf.GetDefaultParameters();

            if (kdf is AesKdf)
            {
                pKdf.SetUInt64(AesKdf.ParamRounds, (ulong)m_numKdfIt.Value);
            }
            else if (kdf is Argon2Kdf)
            {
                ulong uIt = (ulong)m_numKdfIt.Value;
                AdjustKdfParam <ulong>(ref uIt, ">=", Argon2Kdf.MinIterations,
                                       KPRes.Iterations, ref strAdj);
                AdjustKdfParam <ulong>(ref uIt, "<=", Argon2Kdf.MaxIterations,
                                       KPRes.Iterations, ref strAdj);
                pKdf.SetUInt64(Argon2Kdf.ParamIterations, uIt);

                // Adjust parallelism first, as memory depends on it
                uint uPar = (uint)m_numKdfPar.Value;
                AdjustKdfParam <uint>(ref uPar, ">=", Argon2Kdf.MinParallelism,
                                      KPRes.Parallelism, ref strAdj);
                uint uParMax = Argon2Kdf.MaxParallelism;
                int  cp      = Environment.ProcessorCount;
                if ((cp > 0) && (cp <= (int.MaxValue / 2)))
                {
                    uParMax = Math.Min(uParMax, (uint)(cp * 2));
                }
                AdjustKdfParam <uint>(ref uPar, "<=", uParMax,
                                      KPRes.Parallelism, ref strAdj);
                pKdf.SetUInt32(Argon2Kdf.ParamParallelism, uPar);

                ulong uMem     = (ulong)m_numKdfMem.Value;
                int   iMemUnit = m_cmbKdfMem.SelectedIndex;
                while (iMemUnit > 0)
                {
                    if (uMem > (ulong.MaxValue / 1024UL))
                    {
                        uMem = ulong.MaxValue;
                        break;
                    }

                    uMem *= 1024UL;
                    --iMemUnit;
                }

                // 8*p blocks = 1024*8*p bytes minimum memory, see spec
                Debug.Assert(Argon2Kdf.MinMemory == (1024UL * 8UL));
                ulong uMemMin = Argon2Kdf.MinMemory * uPar;
                AdjustKdfParam <ulong>(ref uMem, ">=", uMemMin,
                                       KPRes.Memory, ref strAdj);
                AdjustKdfParam <ulong>(ref uMem, "<=", Argon2Kdf.MaxMemory,
                                       KPRes.Memory, ref strAdj);
                pKdf.SetUInt64(Argon2Kdf.ParamMemory, uMem);
            }
            else
            {
                return(null);             // Plugins may handle it
            }
            if (bShowAdjustments && (strAdj.Length > 0))
            {
                strAdj = KPRes.KdfAdjust + MessageService.NewParagraph + strAdj;
                MessageService.ShowInfo(strAdj);
            }

            return(pKdf);
        }
Esempio n. 7
0
        private void OnBtnOK(object sender, EventArgs e)
        {
            m_pwDatabase.SettingsChanged = DateTime.UtcNow;

            if (m_tbDbName.Text != m_pwDatabase.Name)
            {
                m_pwDatabase.Name        = m_tbDbName.Text;
                m_pwDatabase.NameChanged = DateTime.UtcNow;
            }

            string strNew    = m_tbDbDesc.Text;
            string strOrgFlt = StrUtil.NormalizeNewLines(m_pwDatabase.Description, false);
            string strNewFlt = StrUtil.NormalizeNewLines(strNew, false);

            if (strNewFlt != strOrgFlt)
            {
                m_pwDatabase.Description        = strNew;
                m_pwDatabase.DescriptionChanged = DateTime.UtcNow;
            }

            if (m_tbDefaultUser.Text != m_pwDatabase.DefaultUserName)
            {
                m_pwDatabase.DefaultUserName        = m_tbDefaultUser.Text;
                m_pwDatabase.DefaultUserNameChanged = DateTime.UtcNow;
            }

            if (!m_cbColor.Checked)
            {
                m_pwDatabase.Color = Color.Empty;
            }
            else
            {
                m_pwDatabase.Color = m_clr;
            }

            int nCipher = CipherPool.GlobalPool.GetCipherIndex(m_cmbEncAlgo.Text);

            Debug.Assert(nCipher >= 0);
            if (nCipher >= 0)
            {
                m_pwDatabase.DataCipherUuid = CipherPool.GlobalPool[nCipher].CipherUuid;
            }
            else
            {
                m_pwDatabase.DataCipherUuid = StandardAesEngine.AesUuid;
            }

            // m_pwDatabase.KeyEncryptionRounds = (ulong)m_numKdfIt.Value;
            KdfParameters pKdf = GetKdfParameters(true);

            if (pKdf != null)
            {
                m_pwDatabase.KdfParameters = pKdf;
            }
            // No assert, plugins may assign KDF parameters

            if (m_rbNone.Checked)
            {
                m_pwDatabase.Compression = PwCompressionAlgorithm.None;
            }
            else if (m_rbGZip.Checked)
            {
                m_pwDatabase.Compression = PwCompressionAlgorithm.GZip;
            }
            else
            {
                Debug.Assert(false);
            }

            // m_pwDatabase.MemoryProtection.ProtectTitle = UpdateMemoryProtection(0,
            //	m_pwDatabase.MemoryProtection.ProtectTitle, PwDefs.TitleField);
            // m_pwDatabase.MemoryProtection.ProtectUserName = UpdateMemoryProtection(1,
            //	m_pwDatabase.MemoryProtection.ProtectUserName, PwDefs.UserNameField);
            // m_pwDatabase.MemoryProtection.ProtectPassword = UpdateMemoryProtection(2,
            //	m_pwDatabase.MemoryProtection.ProtectPassword, PwDefs.PasswordField);
            // m_pwDatabase.MemoryProtection.ProtectUrl = UpdateMemoryProtection(3,
            //	m_pwDatabase.MemoryProtection.ProtectUrl, PwDefs.UrlField);
            // m_pwDatabase.MemoryProtection.ProtectNotes = UpdateMemoryProtection(4,
            //	m_pwDatabase.MemoryProtection.ProtectNotes, PwDefs.NotesField);

            // m_pwDatabase.MemoryProtection.AutoEnableVisualHiding = m_cbAutoEnableHiding.Checked;

            if (m_cbRecycleBin.Checked != m_pwDatabase.RecycleBinEnabled)
            {
                m_pwDatabase.RecycleBinEnabled = m_cbRecycleBin.Checked;
                m_pwDatabase.RecycleBinChanged = DateTime.UtcNow;
            }
            int iRecBinSel = m_cmbRecycleBin.SelectedIndex;

            if (m_dictRecycleBinGroups.ContainsKey(iRecBinSel))
            {
                if (!m_dictRecycleBinGroups[iRecBinSel].Equals(m_pwDatabase.RecycleBinUuid))
                {
                    m_pwDatabase.RecycleBinUuid    = m_dictRecycleBinGroups[iRecBinSel];
                    m_pwDatabase.RecycleBinChanged = DateTime.UtcNow;
                }
            }
            else
            {
                Debug.Assert(false);
                if (!PwUuid.Zero.Equals(m_pwDatabase.RecycleBinUuid))
                {
                    m_pwDatabase.RecycleBinUuid    = PwUuid.Zero;
                    m_pwDatabase.RecycleBinChanged = DateTime.UtcNow;
                }
            }

            int iTemplSel = m_cmbEntryTemplates.SelectedIndex;

            if (m_dictEntryTemplateGroups.ContainsKey(iTemplSel))
            {
                if (!m_dictEntryTemplateGroups[iTemplSel].Equals(m_pwDatabase.EntryTemplatesGroup))
                {
                    m_pwDatabase.EntryTemplatesGroup        = m_dictEntryTemplateGroups[iTemplSel];
                    m_pwDatabase.EntryTemplatesGroupChanged = DateTime.UtcNow;
                }
            }
            else
            {
                Debug.Assert(false);
                if (!PwUuid.Zero.Equals(m_pwDatabase.EntryTemplatesGroup))
                {
                    m_pwDatabase.EntryTemplatesGroup        = PwUuid.Zero;
                    m_pwDatabase.EntryTemplatesGroupChanged = DateTime.UtcNow;
                }
            }

            if (!m_cbHistoryMaxItems.Checked)
            {
                m_pwDatabase.HistoryMaxItems = -1;
            }
            else
            {
                m_pwDatabase.HistoryMaxItems = (int)m_numHistoryMaxItems.Value;
            }

            if (!m_cbHistoryMaxSize.Checked)
            {
                m_pwDatabase.HistoryMaxSize = -1;
            }
            else
            {
                m_pwDatabase.HistoryMaxSize = (long)m_numHistoryMaxSize.Value * 1024 * 1024;
            }

            m_pwDatabase.MaintainBackups();             // Apply new history settings

            if (!m_cbKeyRec.Checked)
            {
                m_pwDatabase.MasterKeyChangeRec = -1;
            }
            else
            {
                m_pwDatabase.MasterKeyChangeRec = (long)m_numKeyRecDays.Value;
            }

            if (!m_cbKeyForce.Checked)
            {
                m_pwDatabase.MasterKeyChangeForce = -1;
            }
            else
            {
                m_pwDatabase.MasterKeyChangeForce = (long)m_numKeyForceDays.Value;
            }

            m_pwDatabase.MasterKeyChangeForceOnce = m_cbKeyForceOnce.Checked;
        }
        public override byte[] ProcessBlock(
            byte[] @in,
            int inOff,
            int inLen)
        {
            if (ForEncryption)
            {
                if (KeyPairGenerator != null)
                {
                    EphemeralKeyPair ephKeyPair = KeyPairGenerator.Generate();

                    PrivParam = ephKeyPair.GetKeyPair().Private;
                    V         = ephKeyPair.GetEncodedPublicKey();
                }
            }
            else
            {
                if (KeyParser != null)
                {
                    MemoryStream bIn = new MemoryStream(@in, inOff, inLen)
                    {
                        Position = SecureHeadSize
                    };
                    try
                    {
                        PubParam = KeyParser.ReadKey(bIn);
                    }
                    catch (IOException e)
                    {
                        throw new InvalidCipherTextException("unable to recover ephemeral public key: " + e.Message, e);
                    }
                    catch (ArgumentException e)
                    {
                        throw new InvalidCipherTextException("unable to recover ephemeral public key: " + e.Message, e);
                    }

                    int encLength = (inLen - (int)(bIn.Length - bIn.Position));
                    V = Arrays.CopyOfRange(@in, inOff + SecureHeadSize, inOff + encLength);
                }
            }

            // Compute the common value and convert to byte array.
            Agree.Init(PrivParam);
            BigInteger z = Agree.CalculateAgreement(PubParam);

            byte[] bigZ = BigIntegers.AsUnsignedByteArray(Agree.GetFieldSize(), z);

            try
            {
                // Initialise the KDF.
                KdfParameters kdfParam = new KdfParameters(bigZ, null);
                Kdf.Init(kdfParam);

                byte[] temp = new byte[inLen - SecureHeadSize];

                Array.Copy(@in, inOff + SecureHeadSize, temp, 0, temp.Length);

                return(ForEncryption
                    ? EncryptBlock(@in, inOff, inLen)
                    : DecryptBlock(temp, inOff, temp.Length));
            }
            finally
            {
                Arrays.Fill(bigZ, 0);
            }
        }
Esempio n. 9
0
        private byte[] DecryptBlock(
            byte[]  in_enc,
            int inOff,
            int inLen,
            byte[]  z)
        {
            byte[]        M          = null;
            KeyParameter  macKey     = null;
            KdfParameters kParam     = new KdfParameters(z, param.GetDerivationV());
            int           macKeySize = param.MacKeySize;

            kdf.Init(kParam);

            // Ensure that the length of the input is greater than the MAC in bytes
            if (inLen < mac.GetMacSize())
            {
                throw new InvalidCipherTextException("Length of input must be greater than the MAC");
            }

            inLen -= mac.GetMacSize();

            if (cipher == null)     // stream mode
            {
                byte[] Buffer = GenerateKdfBytes(kParam, inLen + (macKeySize / 8));

                M = new byte[inLen];

                for (int i = 0; i != inLen; i++)
                {
                    M[i] = (byte)(in_enc[inOff + i] ^ Buffer[i]);
                }

                macKey = new KeyParameter(Buffer, inLen, (macKeySize / 8));
            }
            else
            {
                int    cipherKeySize = ((IesWithCipherParameters)param).CipherKeySize;
                byte[] Buffer        = GenerateKdfBytes(kParam, (cipherKeySize / 8) + (macKeySize / 8));

                cipher.Init(false, new KeyParameter(Buffer, 0, (cipherKeySize / 8)));

                M = cipher.DoFinal(in_enc, inOff, inLen);

                macKey = new KeyParameter(Buffer, (cipherKeySize / 8), (macKeySize / 8));
            }

            byte[] macIV = param.GetEncodingV();

            mac.Init(macKey);
            mac.BlockUpdate(in_enc, inOff, inLen);
            mac.BlockUpdate(macIV, 0, macIV.Length);
            mac.DoFinal(macBuf, 0);

            inOff += inLen;

            byte[] T1 = Arrays.CopyOfRange(in_enc, inOff, inOff + macBuf.Length);

            if (!Arrays.ConstantTimeAreEqual(T1, macBuf))
            {
                throw (new InvalidCipherTextException("Invalid MAC."));
            }

            return(M);
        }