14c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrompackage org.bouncycastle.jcajce.provider.asymmetric.dh;
24c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
34c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstromimport java.io.IOException;
44c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstromimport java.io.ObjectInputStream;
54c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstromimport java.io.ObjectOutputStream;
64c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstromimport java.math.BigInteger;
74c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
84c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstromimport javax.crypto.interfaces.DHPublicKey;
94c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstromimport javax.crypto.spec.DHParameterSpec;
104c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstromimport javax.crypto.spec.DHPublicKeySpec;
114c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
124c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstromimport org.bouncycastle.asn1.ASN1Integer;
134c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstromimport org.bouncycastle.asn1.ASN1ObjectIdentifier;
144c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstromimport org.bouncycastle.asn1.ASN1Sequence;
154c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstromimport org.bouncycastle.asn1.pkcs.DHParameter;
164c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstromimport org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
174c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstromimport org.bouncycastle.asn1.x509.AlgorithmIdentifier;
184c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstromimport org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
194c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstromimport org.bouncycastle.asn1.x9.DHDomainParameters;
204c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstromimport org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
214c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstromimport org.bouncycastle.crypto.params.DHPublicKeyParameters;
224c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstromimport org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
234c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
244c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrompublic class BCDHPublicKey
254c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    implements DHPublicKey
264c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom{
274c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    static final long serialVersionUID = -216691575254424324L;
284c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
294c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    private BigInteger              y;
304c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
314c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    private transient DHParameterSpec         dhSpec;
324c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    private transient SubjectPublicKeyInfo    info;
334c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
344c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    BCDHPublicKey(
354c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        DHPublicKeySpec spec)
364c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    {
374c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        this.y = spec.getY();
384c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        this.dhSpec = new DHParameterSpec(spec.getP(), spec.getG());
394c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    }
404c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
414c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    BCDHPublicKey(
424c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        DHPublicKey key)
434c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    {
444c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        this.y = key.getY();
454c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        this.dhSpec = key.getParams();
464c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    }
474c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
484c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    BCDHPublicKey(
494c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        DHPublicKeyParameters params)
504c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    {
514c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        this.y = params.getY();
524c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        this.dhSpec = new DHParameterSpec(params.getParameters().getP(), params.getParameters().getG(), params.getParameters().getL());
534c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    }
544c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
554c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    BCDHPublicKey(
564c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        BigInteger y,
574c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        DHParameterSpec dhSpec)
584c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    {
594c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        this.y = y;
604c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        this.dhSpec = dhSpec;
614c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    }
624c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
634c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    public BCDHPublicKey(
644c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        SubjectPublicKeyInfo info)
654c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    {
664c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        this.info = info;
674c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
684c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        ASN1Integer              derY;
694c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        try
704c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        {
714c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            derY = (ASN1Integer)info.parsePublicKey();
724c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        }
734c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        catch (IOException e)
744c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        {
754c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            throw new IllegalArgumentException("invalid info structure in DH public key");
764c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        }
774c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
784c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        this.y = derY.getValue();
794c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
804c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        ASN1Sequence seq = ASN1Sequence.getInstance(info.getAlgorithm().getParameters());
814c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        ASN1ObjectIdentifier id = info.getAlgorithm().getAlgorithm();
824c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
834c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        // we need the PKCS check to handle older keys marked with the X9 oid.
844c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        if (id.equals(PKCSObjectIdentifiers.dhKeyAgreement) || isPKCSParam(seq))
854c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        {
864c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            DHParameter             params = DHParameter.getInstance(seq);
874c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
884c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            if (params.getL() != null)
894c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            {
904c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom                this.dhSpec = new DHParameterSpec(params.getP(), params.getG(), params.getL().intValue());
914c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            }
924c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            else
934c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            {
944c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom                this.dhSpec = new DHParameterSpec(params.getP(), params.getG());
954c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            }
964c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        }
974c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        else if (id.equals(X9ObjectIdentifiers.dhpublicnumber))
984c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        {
994c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            DHDomainParameters params = DHDomainParameters.getInstance(seq);
1004c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1014c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            this.dhSpec = new DHParameterSpec(params.getP().getValue(), params.getG().getValue());
1024c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        }
1034c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        else
1044c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        {
1054c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            throw new IllegalArgumentException("unknown algorithm type: " + id);
1064c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        }
1074c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    }
1084c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1094c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    public String getAlgorithm()
1104c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    {
1114c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        return "DH";
1124c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    }
1134c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1144c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    public String getFormat()
1154c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    {
1164c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        return "X.509";
1174c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    }
1184c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1194c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    public byte[] getEncoded()
1204c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    {
1214c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        if (info != null)
1224c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        {
1234c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            return KeyUtil.getEncodedSubjectPublicKeyInfo(info);
1244c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        }
1254c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1264c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        return KeyUtil.getEncodedSubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.dhKeyAgreement, new DHParameter(dhSpec.getP(), dhSpec.getG(), dhSpec.getL()).toASN1Primitive()), new ASN1Integer(y));
1274c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    }
1284c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1294c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    public DHParameterSpec getParams()
1304c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    {
1314c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        return dhSpec;
1324c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    }
1334c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1344c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    public BigInteger getY()
1354c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    {
1364c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        return y;
1374c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    }
1384c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1394c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    private boolean isPKCSParam(ASN1Sequence seq)
1404c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    {
1414c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        if (seq.size() == 2)
1424c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        {
1434c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            return true;
1444c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        }
1454c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1464c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        if (seq.size() > 3)
1474c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        {
1484c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            return false;
1494c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        }
1504c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1514c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        ASN1Integer l = ASN1Integer.getInstance(seq.getObjectAt(2));
1524c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        ASN1Integer p = ASN1Integer.getInstance(seq.getObjectAt(0));
1534c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1544c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        if (l.getValue().compareTo(BigInteger.valueOf(p.getValue().bitLength())) > 0)
1554c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        {
1564c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            return false;
1574c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        }
1584c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1594c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        return true;
1604c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    }
1614c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1624c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    public int hashCode()
1634c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    {
1644c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        return this.getY().hashCode() ^ this.getParams().getG().hashCode()
1654c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom                ^ this.getParams().getP().hashCode() ^ this.getParams().getL();
1664c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    }
1674c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1684c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    public boolean equals(
1694c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        Object o)
1704c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    {
1714c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        if (!(o instanceof DHPublicKey))
1724c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        {
1734c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            return false;
1744c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        }
1754c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1764c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        DHPublicKey other = (DHPublicKey)o;
1774c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1784c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        return this.getY().equals(other.getY())
1794c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            && this.getParams().getG().equals(other.getParams().getG())
1804c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            && this.getParams().getP().equals(other.getParams().getP())
1814c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            && this.getParams().getL() == other.getParams().getL();
1824c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    }
1834c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1844c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    private void readObject(
1854c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        ObjectInputStream   in)
1864c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        throws IOException, ClassNotFoundException
1874c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    {
1884c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        in.defaultReadObject();
1894c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1904c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        this.dhSpec = new DHParameterSpec((BigInteger)in.readObject(), (BigInteger)in.readObject(), in.readInt());
1914c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        this.info = null;
1924c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    }
1934c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1944c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    private void writeObject(
1954c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        ObjectOutputStream  out)
1964c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        throws IOException
1974c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    {
1984c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        out.defaultWriteObject();
1994c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
2004c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        out.writeObject(dhSpec.getP());
2014c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        out.writeObject(dhSpec.getG());
2024c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        out.writeInt(dhSpec.getL());
2034c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    }
2044c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom}
205