Skip to content

Commit

Permalink
Fix unpacking time (unsigned int) from octets for large values
Browse files Browse the repository at this point in the history
  • Loading branch information
vanitasvitae committed Sep 14, 2024
1 parent 7352222 commit e50ddc6
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 11 deletions.
4 changes: 2 additions & 2 deletions pg/src/main/java/org/bouncycastle/bcpg/PublicKeyPacket.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ public class PublicKeyPacket
throw new UnsupportedPacketVersionException("Unsupported Public Key Packet version encountered: " + version);
}

time = StreamUtil.read4OctetLength(in);
time = StreamUtil.readSeconds(in);

if (version == 2 || version == VERSION_3)
{
Expand Down Expand Up @@ -324,7 +324,7 @@ public byte[] getEncodedContents()

pOut.write(version);

StreamUtil.writeTime(pOut, time);
StreamUtil.writeSeconds(pOut, time);

if (version <= VERSION_3)
{
Expand Down
6 changes: 2 additions & 4 deletions pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class SignaturePacket

private int version;
private int signatureType;
private long creationTime;
private long creationTime; // millis
private long keyID;
private int keyAlgorithm;
private int hashAlgorithm;
Expand Down Expand Up @@ -647,10 +647,8 @@ public void encode(
{
pOut.write(5); // the length of the next block

long time = creationTime / 1000;

pOut.write(signatureType);
StreamUtil.writeTime(pOut, time);
StreamUtil.writeTime(pOut, creationTime);

StreamUtil.writeKeyID(pOut, keyID);

Expand Down
19 changes: 16 additions & 3 deletions pg/src/main/java/org/bouncycastle/bcpg/StreamUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,16 +108,29 @@ static long readKeyID(BCPGInputStream in)
return keyID | in.read();
}

static void writeTime(BCPGOutputStream pOut, long time)
throws IOException
static long readSeconds(BCPGInputStream in)
throws IOException
{
return ((long)read4OctetLength(in)) & 0xFFFFFFFFL;
}

static void writeSeconds(BCPGOutputStream pOut, long time)
throws IOException
{
StreamUtil.write4OctetLength(pOut, (int)time);
}

static long readTime(BCPGInputStream in)
throws IOException
{
return (long)read4OctetLength(in) * 1000L;
long s = ((long)read4OctetLength(in)) & 0xFFFFFFFFL;
return s * 1000L;
}

static void writeTime(BCPGOutputStream pOut, long time)
throws IOException
{
StreamUtil.write4OctetLength(pOut, (int) (time / 1000));
}

static void write2OctetLength(OutputStream pOut, int len)
Expand Down
2 changes: 1 addition & 1 deletion pg/src/main/java/org/bouncycastle/bcpg/sig/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ static long timeFromBytes(byte[] bytes)
throw new IllegalStateException("Byte array has unexpected length. Expected length 4, got " + bytes.length);
}

return Pack.bigEndianToInt(bytes, 0);
return Pack.bigEndianToInt(bytes, 0) & 0xFFFFFFFFL; // time is unsigned
}

static byte[] timeToBytes(long t)
Expand Down
3 changes: 2 additions & 1 deletion pg/src/test/java/org/bouncycastle/bcpg/test/AllTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ public void testPacketParsing()
new OnePassSignaturePacketTest(),
new OpenPgpMessageTest(),
new FingerprintUtilTest(),
new EncryptedMessagePacketTest()
new EncryptedMessagePacketTest(),
new TimeEncodingTest()
};

for (int i = 0; i != tests.length; i++)
Expand Down
70 changes: 70 additions & 0 deletions pg/src/test/java/org/bouncycastle/bcpg/test/TimeEncodingTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package org.bouncycastle.bcpg.test;

import org.bouncycastle.bcpg.BCPGInputStream;
import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.bcpg.PacketFormat;
import org.bouncycastle.bcpg.PublicKeyPacket;
import org.bouncycastle.bcpg.UnknownBCPGKey;
import org.bouncycastle.bcpg.sig.KeyExpirationTime;
import org.bouncycastle.util.test.SimpleTest;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Date;

public class TimeEncodingTest
extends SimpleTest
{
@Override
public String getName()
{
return "UtilsTest";
}

@Override
public void performTest()
throws Exception
{
testRoundtrippingLargeUnsignedInt();
testKeyWithLargeCreationTime();
}

private void testRoundtrippingLargeUnsignedInt()
{
// Integer.MAX_VALUE < large < 0xffffffff
long large = 2523592696L; // fits a 32-bit *unsigned* int, but overflows signed int
// KeyExpirationTime packs the time into 4 octets
KeyExpirationTime kexp = new KeyExpirationTime(false, large);
// getTime() parses the time from 4 octets
isEquals("Roundtripped unsigned int mismatches before packet parser pass", large, kexp.getTime());

// To be safe, do an additional packet encode/decode roundtrip
KeyExpirationTime pKexp = new KeyExpirationTime(kexp.isCritical(), kexp.isLongLength(), kexp.getData());
isEquals("Roundtripped unsigned int mismatches after packet parser pass", large, pKexp.getTime());
}

private void testKeyWithLargeCreationTime()
throws IOException
{
long maxSeconds = 0xFFFFFFFEL; // Fits 32 unsigned int, but not signed int
Date maxPGPDate = new Date(maxSeconds * 1000);
UnknownBCPGKey k = new UnknownBCPGKey(1, new byte[]{1}); // dummy
PublicKeyPacket p = new PublicKeyPacket(PublicKeyPacket.VERSION_6, 99, maxPGPDate, k);
isEquals("Key creation time mismatches before encoding", maxPGPDate, p.getTime());

ByteArrayOutputStream bOut = new ByteArrayOutputStream();
BCPGOutputStream pOut = new BCPGOutputStream(bOut, PacketFormat.CURRENT);
p.encode(pOut);
pOut.close();
ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray());
BCPGInputStream pIn = new BCPGInputStream(bIn);
PublicKeyPacket parsed = (PublicKeyPacket) pIn.readPacket();
isEquals("Key creation time mismatches after encoding", maxPGPDate, parsed.getTime());
}

public static void main(String[] args)
{
runTest(new TimeEncodingTest());
}
}

0 comments on commit e50ddc6

Please sign in to comment.