using System; using System.IO; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Bcpg.OpenPgp { /// A one pass signature object. public class PgpOnePassSignature { private static OnePassSignaturePacket Cast(Packet packet) { if (packet is OnePassSignaturePacket onePassSignaturePacket) return onePassSignaturePacket; throw new IOException("unexpected packet in stream: " + packet); } private readonly OnePassSignaturePacket sigPack; private readonly int signatureType; private ISigner sig; private byte lastb; internal PgpOnePassSignature( BcpgInputStream bcpgInput) : this(Cast(bcpgInput.ReadPacket())) { } internal PgpOnePassSignature( OnePassSignaturePacket sigPack) { this.sigPack = sigPack; this.signatureType = sigPack.SignatureType; } /// Initialise the signature object for verification. public void InitVerify(PgpPublicKey pubKey) { lastb = 0; AsymmetricKeyParameter key = pubKey.GetKey(); try { sig = PgpUtilities.CreateSigner(sigPack.KeyAlgorithm, sigPack.HashAlgorithm, key); } catch (Exception e) { throw new PgpException("can't set up signature object.", e); } try { sig.Init(false, key); } catch (InvalidKeyException e) { throw new PgpException("invalid key.", e); } } public void Update(byte b) { if (signatureType == PgpSignature.CanonicalTextDocument) { DoCanonicalUpdateByte(b); } else { sig.Update(b); } } private void DoCanonicalUpdateByte(byte b) { if (b == '\r') { DoUpdateCRLF(); } else if (b == '\n') { if (lastb != '\r') { DoUpdateCRLF(); } } else { sig.Update(b); } lastb = b; } private void DoUpdateCRLF() { sig.Update((byte)'\r'); sig.Update((byte)'\n'); } public void Update(params byte[] bytes) { Update(bytes, 0, bytes.Length); } public void Update(byte[] bytes, int off, int length) { #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER Update(bytes.AsSpan(off, length)); #else if (signatureType == PgpSignature.CanonicalTextDocument) { int finish = off + length; for (int i = off; i != finish; i++) { DoCanonicalUpdateByte(bytes[i]); } } else { sig.BlockUpdate(bytes, off, length); } #endif } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER public void Update(ReadOnlySpan input) { if (signatureType == PgpSignature.CanonicalTextDocument) { for (int i = 0; i < input.Length; ++i) { DoCanonicalUpdateByte(input[i]); } } else { sig.BlockUpdate(input); } } #endif /// Verify the calculated signature against the passed in PgpSignature. public bool Verify(PgpSignature pgpSig) { byte[] trailer = pgpSig.GetSignatureTrailer(); sig.BlockUpdate(trailer, 0, trailer.Length); return sig.VerifySignature(pgpSig.GetSignature()); } public long KeyId { get { return sigPack.KeyId; } } public int SignatureType { get { return sigPack.SignatureType; } } public HashAlgorithmTag HashAlgorithm { get { return sigPack.HashAlgorithm; } } public PublicKeyAlgorithmTag KeyAlgorithm { get { return sigPack.KeyAlgorithm; } } public byte[] GetEncoded() { var bOut = new MemoryStream(); Encode(bOut); return bOut.ToArray(); } public void Encode(Stream outStr) { BcpgOutputStream.Wrap(outStr).WritePacket(sigPack); } } }