summary refs log tree commit diff
path: root/crypto/src/openpgp/Rfc6637Utilities.cs
blob: e1405f4812f28100ee8a4c24661e204931a7559f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
using System;
using System.IO;

using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Encoders;

namespace Org.BouncyCastle.Bcpg.OpenPgp
{
    public sealed class Rfc6637Utilities
    {
        private Rfc6637Utilities()
        {
        }

        // "Anonymous Sender    ", which is the octet sequence
        private static readonly byte[] ANONYMOUS_SENDER = Hex.Decode("416E6F6E796D6F75732053656E64657220202020");

        public static string GetAgreementAlgorithm(PublicKeyPacket pubKeyData)
        {
            ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)pubKeyData.Key;

            switch (ecKey.HashAlgorithm)
            {
            case HashAlgorithmTag.Sha256:
                return "ECCDHwithSHA256CKDF";
            case HashAlgorithmTag.Sha384:
                return "ECCDHwithSHA384CKDF";
            case HashAlgorithmTag.Sha512:
                return "ECCDHwithSHA512CKDF";
            default:
                throw new ArgumentException("Unknown hash algorithm specified: " + ecKey.HashAlgorithm);
            }
        }

        public static DerObjectIdentifier GetKeyEncryptionOID(SymmetricKeyAlgorithmTag algID)
        {
            switch (algID)
            {
            case SymmetricKeyAlgorithmTag.Aes128:
                return NistObjectIdentifiers.IdAes128Wrap;
            case SymmetricKeyAlgorithmTag.Aes192:
                return NistObjectIdentifiers.IdAes192Wrap;
            case SymmetricKeyAlgorithmTag.Aes256:
                return NistObjectIdentifiers.IdAes256Wrap;
            default:
                throw new PgpException("unknown symmetric algorithm ID: " + algID);
            }
        }

        public static int GetKeyLength(SymmetricKeyAlgorithmTag algID)
        {
            switch (algID)
            {
            case SymmetricKeyAlgorithmTag.Aes128:
                return 16;
            case SymmetricKeyAlgorithmTag.Aes192:
                return 24;
            case SymmetricKeyAlgorithmTag.Aes256:
                return 32;
            default:
                throw new PgpException("unknown symmetric algorithm ID: " + algID);
            }
        }

        public static byte[] CreateKey(PublicKeyPacket pubKeyData, ECPoint s)
        {
            return CreateKey(pubKeyData, s.AffineXCoord.GetEncoded());
        }

        public static byte[] CreateKey(PublicKeyPacket pubKeyData, byte[] secret)
        {
            byte[] userKeyingMaterial = CreateUserKeyingMaterial(pubKeyData);

            ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)pubKeyData.Key;

            return Kdf(ecKey.HashAlgorithm, secret, GetKeyLength(ecKey.SymmetricKeyAlgorithm), userKeyingMaterial);
        }

        // RFC 6637 - Section 8
        // curve_OID_len = (byte)len(curve_OID);
        // Param = curve_OID_len || curve_OID || public_key_alg_ID || 03
        // || 01 || KDF_hash_ID || KEK_alg_ID for AESKeyWrap || "Anonymous
        // Sender    " || recipient_fingerprint;
        // Z_len = the key size for the KEK_alg_ID used with AESKeyWrap
        // Compute Z = KDF( S, Z_len, Param );
        public static byte[] CreateUserKeyingMaterial(PublicKeyPacket pubKeyData)
        {
            MemoryStream pOut = new MemoryStream();
            ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)pubKeyData.Key;
            byte[] encOid = ecKey.CurveOid.GetEncoded();

            pOut.Write(encOid, 1, encOid.Length - 1);
            pOut.WriteByte((byte)pubKeyData.Algorithm);
            pOut.WriteByte(0x03);
            pOut.WriteByte(0x01);
            pOut.WriteByte((byte)ecKey.HashAlgorithm);
            pOut.WriteByte((byte)ecKey.SymmetricKeyAlgorithm);
            pOut.Write(ANONYMOUS_SENDER, 0, ANONYMOUS_SENDER.Length);

            byte[] fingerprint = PgpPublicKey.CalculateFingerprint(pubKeyData);
            pOut.Write(fingerprint, 0, fingerprint.Length);

            return pOut.ToArray();
        }

        // RFC 6637 - Section 7
        //   Implements KDF( X, oBits, Param );
        //   Input: point X = (x,y)
        //   oBits - the desired size of output
        //   hBits - the size of output of hash function Hash
        //   Param - octets representing the parameters
        //   Assumes that oBits <= hBits
        //   Convert the point X to the octet string, see section 6:
        //   ZB' = 04 || x || y
        //   and extract the x portion from ZB'
        //         ZB = x;
        //         MB = Hash ( 00 || 00 || 00 || 01 || ZB || Param );
        //   return oBits leftmost bits of MB.
        private static byte[] Kdf(HashAlgorithmTag digestAlg, byte[] ZB, int keyLen, byte[] parameters)
        {
            IDigest digest = PgpUtilities.CreateDigest(digestAlg);

            digest.Update(0x00);
            digest.Update(0x00);
            digest.Update(0x00);
            digest.Update(0x01);
            digest.BlockUpdate(ZB, 0, ZB.Length);
            digest.BlockUpdate(parameters, 0, parameters.Length);

            byte[] hash = DigestUtilities.DoFinal(digest);

            return Arrays.CopyOfRange(hash, 0, keyLen);
        }
    }
}