public static (ushort size, byte[] name) NameFromTPM2BName(byte[] ab, ref int offset) { // TCG TPM Rev 2.0, part 2, structures, section 10.5.3, TPM2B_NAME // This buffer holds a Name for any entity type. // The type of Name in the structure is determined by context and the size parameter. var totalBytes = AuthDataHelper.GetSizedByteArray(ab, ref offset, 2); ushort totalSize = 0; if (null != totalBytes) { totalSize = BitConverter.ToUInt16(totalBytes.ToArray().Reverse().ToArray(), 0); } ushort size = 0; var bytes = AuthDataHelper.GetSizedByteArray(ab, ref offset, 2); if (null != bytes) { size = BitConverter.ToUInt16(bytes.ToArray().Reverse().ToArray(), 0); } // If size is four, then the Name is a handle. if (4 == size) { throw new VerificationException("Unexpected handle in TPM2B_NAME"); } // If size is zero, then no Name is present. if (0 == size) { throw new VerificationException("Unexpected no name found in TPM2B_NAME"); } // Otherwise, the size shall be the size of a TPM_ALG_ID plus the size of the digest produced by the indicated hash algorithm. byte[] name = null; if (Enum.IsDefined(typeof(TpmAlg), size)) { var tpmalg = (TpmAlg)size; if (tpmAlgToDigestSizeMap.ContainsKey(tpmalg)) { name = AuthDataHelper.GetSizedByteArray(ab, ref offset, tpmAlgToDigestSizeMap[tpmalg]); } else { throw new VerificationException("TPM_ALG_ID found in TPM2B_NAME not acceptable hash algorithm"); } } else { throw new VerificationException("Invalid TPM_ALG_ID found in TPM2B_NAME"); } if (totalSize != bytes.Length + name.Length) { throw new VerificationException("Unexpected extra bytes found in TPM2B_NAME"); } return(size, name); }
public CertInfo(byte[] certInfo) { if (null == certInfo || 0 == certInfo.Length) { throw new VerificationException("Malformed certInfo bytes"); } Raw = certInfo; var offset = 0; Magic = AuthDataHelper.GetSizedByteArray(certInfo, ref offset, 4); if (0xff544347 != BitConverter.ToUInt32(Magic.ToArray().Reverse().ToArray(), 0)) { throw new VerificationException("Bad magic number " + BitConverter.ToString(Magic).Replace("-", "")); } Type = AuthDataHelper.GetSizedByteArray(certInfo, ref offset, 2); if (0x8017 != BitConverter.ToUInt16(Type.ToArray().Reverse().ToArray(), 0)) { throw new VerificationException("Bad structure tag " + BitConverter.ToString(Type).Replace("-", "")); } QualifiedSigner = AuthDataHelper.GetSizedByteArray(certInfo, ref offset); ExtraData = AuthDataHelper.GetSizedByteArray(certInfo, ref offset); if (null == ExtraData || 0 == ExtraData.Length) { throw new VerificationException("Bad extraData in certInfo"); } Clock = AuthDataHelper.GetSizedByteArray(certInfo, ref offset, 8); ResetCount = AuthDataHelper.GetSizedByteArray(certInfo, ref offset, 4); RestartCount = AuthDataHelper.GetSizedByteArray(certInfo, ref offset, 4); Safe = AuthDataHelper.GetSizedByteArray(certInfo, ref offset, 1); FirmwareVersion = AuthDataHelper.GetSizedByteArray(certInfo, ref offset, 8); var(size, name) = NameFromTPM2BName(certInfo, ref offset); Alg = size; // TPM_ALG_ID AttestedName = name; AttestedQualifiedNameBuffer = AuthDataHelper.GetSizedByteArray(certInfo, ref offset); if (certInfo.Length != offset) { throw new VerificationException("Leftover bits decoding certInfo"); } }
public PubArea(byte[] pubArea) { Raw = pubArea; var offset = 0; // TPMI_ALG_PUBLIC Type = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 2); var tpmalg = (TpmAlg)Enum.Parse(typeof(TpmAlg), BitConverter.ToUInt16(Type.Reverse().ToArray(), 0).ToString()); // TPMI_ALG_HASH Alg = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 2); // TPMA_OBJECT, attributes that, along with type, determine the manipulations of this object Attributes = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 4); // TPM2B_DIGEST, optional policy for using this key, computed using the alg of the object Policy = AuthDataHelper.GetSizedByteArray(pubArea, ref offset); // TPMU_PUBLIC_PARMS Symmetric = null; Scheme = null; if (TpmAlg.TPM_ALG_KEYEDHASH == tpmalg) { throw new VerificationException("TPM_ALG_KEYEDHASH not yet supported"); } if (TpmAlg.TPM_ALG_SYMCIPHER == tpmalg) { throw new VerificationException("TPM_ALG_SYMCIPHER not yet supported"); } // TPMS_ASYM_PARMS, for TPM_ALG_RSA and TPM_ALG_ECC if (TpmAlg.TPM_ALG_RSA == tpmalg || TpmAlg.TPM_ALG_ECC == tpmalg) { Symmetric = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 2); Scheme = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 2); } // TPMI_RSA_KEY_BITS, number of bits in the public modulus KeyBits = null; // The public exponent, a prime number greater than 2. When zero, indicates that the exponent is the default of 2^16 + 1 Exponent = 0; if (TpmAlg.TPM_ALG_RSA == tpmalg) { KeyBits = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 2); var tmp = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 4); if (null != tmp) { Exponent = BitConverter.ToUInt32(tmp.ToArray(), 0); if (0 == Exponent) { Exponent = Convert.ToUInt32(Math.Pow(2, 16) + 1); } } } // TPMI_ECC_CURVE CurveID = null; // TPMT_KDF_SCHEME, an optional key derivation scheme for generating a symmetric key from a Z value // If the kdf parameter associated with curveID is not TPM_ALG_NULL then this is required to be NULL. // NOTE There are currently no commands where this parameter has effect and, in the reference code, this field needs to be set to TPM_ALG_NULL. KDF = null; if (TpmAlg.TPM_ALG_ECC == tpmalg) { CurveID = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 2); KDF = AuthDataHelper.GetSizedByteArray(pubArea, ref offset, 2); } // TPMU_PUBLIC_ID Unique = AuthDataHelper.GetSizedByteArray(pubArea, ref offset); if (pubArea.Length != offset) { throw new VerificationException("Leftover bytes decoding pubArea"); } }
public static byte[] GetASN1ObjectAtIndex(byte[] attExtBytes, int index) { // https://developer.android.com/training/articles/security-key-attestation#certificate_schema // This function returns an entry from the KeyDescription at index if (null == attExtBytes || 0 == attExtBytes.Length || attExtBytes.Length > ushort.MaxValue) { throw new Fido2VerificationException("Invalid attExtBytes signature value"); } var offset = 0; var derSequence = AuthDataHelper.GetSizedByteArray(attExtBytes, ref offset, 1); // expecting to start with 0x30 indicating SEQUENCE if (null == derSequence || 0x30 != derSequence[0]) { throw new Fido2VerificationException("attExtBytes signature not a valid DER sequence"); } // next is length of all the items in the sequence var dataLen = AuthDataHelper.GetSizedByteArray(attExtBytes, ref offset, 1); if (null == dataLen) { throw new Fido2VerificationException("attExtBytes signature has invalid length"); } // if data is more than 127 bytes, the length is encoded in long form // TODO : Why is longLen never used ? var longForm = (dataLen[0] > 0x7f); var longLen = 0; if (true == longForm) { var longLenByte = AuthDataHelper.GetSizedByteArray(attExtBytes, ref offset, 1); if (null == longLenByte) { throw new Fido2VerificationException("attExtBytes signature has invalid long form length"); } longLen = longLenByte[0]; longLen &= (1 << 7); } // walk through each sequence entry until we get to the requested index for (var i = 0; i < index; i++) { var derId = AuthDataHelper.GetSizedByteArray(attExtBytes, ref offset, 1); if (null == derId) { throw new Fido2VerificationException("Ran out of bytes in attExtBytes sequence without finding the first octet string"); } var lenValue = AuthDataHelper.GetSizedByteArray(attExtBytes, ref offset, 1); if (null == lenValue) { throw new Fido2VerificationException("attExtBytes lenValue invalid"); } if (0 < lenValue[0]) { var value = AuthDataHelper.GetSizedByteArray(attExtBytes, ref offset, lenValue[0]); if (null == value) { throw new Fido2VerificationException("Ran out of bytes in attExtBytes sequence without finding the first octet string"); } } } // skip the identifier of the requested item var asn1Id = AuthDataHelper.GetSizedByteArray(attExtBytes, ref offset, 1); if (null == asn1Id) { throw new Fido2VerificationException("Ran out of bytes in attExtBytes sequence without finding the first octet string"); } // get length of requested item var lenAsn1value = AuthDataHelper.GetSizedByteArray(attExtBytes, ref offset, 1); if (null == lenAsn1value) { throw new Fido2VerificationException("lenAttestationChallenge version length invalid"); } // return byte array containing the requested item return(AuthDataHelper.GetSizedByteArray(attExtBytes, ref offset, lenAsn1value[0])); }