예제 #1
0
 private static bool VerifyChecksum(
     S2kUsageTag usageTag,
     ReadOnlySpan <byte> data,
     out int checksumLength)
 {
     if (usageTag == S2kUsageTag.Sha1)
     {
         using var sha1 = IncrementalHash.CreateHash(HashAlgorithmName.SHA1);
         sha1.AppendData(data.Slice(0, data.Length - sha1.HashLengthInBytes));
         checksumLength = sha1.HashLengthInBytes;
         return(sha1.GetHashAndReset().AsSpan().SequenceEqual(data.Slice(data.Length - sha1.HashLengthInBytes)));
     }
     else if (usageTag == S2kUsageTag.Checksum)
     {
         int checksum = 0;
         for (int i = 0; i < data.Length - 2; i++)
         {
             checksum += data[i];
         }
         checksumLength = 2;
         return(data[data.Length - 2] == (byte)(checksum >> 8) && data[data.Length - 1] == (byte)checksum);
     }
     else // None
     {
         checksumLength = 0;
         return(true);
     }
 }
예제 #2
0
        public static void DecryptSecretKey(ReadOnlySpan <byte> password, ReadOnlySpan <byte> source, Span <byte> destination, out int bytesWritten, int version = 4)
        {
            S2kUsageTag usageTag = (S2kUsageTag)source[0];
            PgpSymmetricKeyAlgorithm encryptionAlgorithm;
            var              salt           = new ReadOnlySpan <byte>();
            long             iterationCount = 0;
            PgpHashAlgorithm hashAlgorithm;

            if (usageTag == S2kUsageTag.Checksum || usageTag == S2kUsageTag.Sha1 /* || usageTag == S2kUsageTag.Aead */)
            {
                encryptionAlgorithm = (PgpSymmetricKeyAlgorithm)source[1];
                byte s2kType = source[2];
                hashAlgorithm = (PgpHashAlgorithm)source[3];
                source        = source.Slice(4);
                if (s2kType > 0 && s2kType <= 3)
                {
                    salt   = source.Slice(0, 8);
                    source = source.Slice(8);
                    if (s2kType == 3)
                    {
                        iterationCount = (16 + (source[0] & 15)) << ((source[0] >> 4) + 6);
                        source         = source.Slice(1);
                    }
                }
                else if (s2kType == 101) // GNU private
                {
                    throw new NotImplementedException();
                }
                else
                {
                    throw new CryptographicException(); // Unknown S2K type
                }
            }
            else if (usageTag == S2kUsageTag.None)
            {
                // No encryption
                bytesWritten = source.Slice(1).TryCopyTo(destination) ? source.Length : 0;
                return;
            }
            else
            {
                // No salt, no iterations, MD5 hash
                encryptionAlgorithm = (PgpSymmetricKeyAlgorithm)usageTag;
                hashAlgorithm       = PgpHashAlgorithm.MD5;
                usageTag            = S2kUsageTag.Checksum;
                source = source.Slice(1);
            }

            int         keySizeInBytes = (PgpUtilities.GetKeySize(encryptionAlgorithm) + 7) / 8;
            Span <byte> keyBytes       = stackalloc byte[keySizeInBytes];

            try
            {
                MakeKey(password, hashAlgorithm, salt, iterationCount, keyBytes);

                using var c = PgpUtilities.GetSymmetricAlgorithm(encryptionAlgorithm);

                // Read the IV
                c.IV  = source.Slice(0, c.BlockSize / 8).ToArray();
                c.Key = keyBytes.ToArray();

                source = source.Slice(c.BlockSize / 8);

                if (destination.Length < source.Length)
                {
                    bytesWritten = 0;
                    return;
                }

                // Do the actual decryption
                bytesWritten = source.Length;
                if (version == 4)
                {
                    using var decryptor = new ZeroPaddedCryptoTransform(c.CreateDecryptor());
                    var data = decryptor.TransformFinalBlock(source.ToArray(), 0, source.Length);
                    data.AsSpan().CopyTo(destination);
                    CryptographicOperations.ZeroMemory(data);
                }
                else if (version == 3)
                {
                    // Version 3 is four RSA parameters encoded as MPIntegers separately
                    var sourceArray = source.ToArray();
                    int pos         = 0;
                    for (int i = 0; i != 4; i++)
                    {
                        int encLen = ((source[pos] << 8) + source[pos + 1] + 7) / 8;
                        destination[pos]     = source[pos];
                        destination[pos + 1] = source[pos + 1];
                        pos += 2;

                        if (encLen > source.Length - pos)
                        {
                            throw new PgpException("out of range encLen found in encData");
                        }

                        using var decryptor = new ZeroPaddedCryptoTransform(c.CreateDecryptor());
                        var data = decryptor.TransformFinalBlock(sourceArray, pos, encLen);
                        data.CopyTo(destination.Slice(pos));
                        CryptographicOperations.ZeroMemory(data);
                        pos += encLen;

                        if (i != 3)
                        {
                            c.IV = source.Slice(pos - (c.BlockSize / 8), c.BlockSize / 8).ToArray();
                        }
                    }

                    destination[pos]     = source[pos];
                    destination[pos + 1] = source[pos + 1];
                }

                if (!VerifyChecksum(usageTag, destination.Slice(0, bytesWritten), out int checksumLength))
                {
                    throw new CryptographicException();
                }

                bytesWritten -= checksumLength;
            }
            finally
            {
                CryptographicOperations.ZeroMemory(keyBytes);
            }
        }