/*
Copyright 2016 Google Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using Google.Apis.Util;
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
namespace Google.Apis.Auth.OAuth2
{
internal class Pkcs8
{
// PKCS#8 specification: https://www.ietf.org/rfc/rfc5208.txt
// ASN.1 specification: https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
///
/// An incomplete ASN.1 decoder, only implements what's required
/// to decode a Service Credential.
///
internal class Asn1
{
internal enum Tag
{
Integer = 2,
OctetString = 4,
Null = 5,
ObjectIdentifier = 6,
Sequence = 16,
}
internal class Decoder
{
public Decoder(byte[] bytes)
{
_bytes = bytes;
_index = 0;
}
private byte[] _bytes;
private int _index;
public object Decode()
{
Tag tag = ReadTag();
switch (tag)
{
case Tag.Integer:
return ReadInteger();
case Tag.OctetString:
return ReadOctetString();
case Tag.Null:
return ReadNull();
case Tag.ObjectIdentifier:
return ReadOid();
case Tag.Sequence:
return ReadSequence();
default:
throw new NotSupportedException($"Tag '{tag}' not supported.");
}
}
private byte NextByte() => _bytes[_index++];
private byte[] ReadLengthPrefixedBytes()
{
int length = ReadLength();
return ReadBytes(length);
}
private byte[] ReadInteger() => ReadLengthPrefixedBytes();
private object ReadOctetString()
{
byte[] bytes = ReadLengthPrefixedBytes();
return new Decoder(bytes).Decode();
}
private object ReadNull()
{
int length = ReadLength();
if (length != 0)
{
throw new InvalidDataException("Invalid data, Null length must be 0.");
}
return null;
}
private int[] ReadOid()
{
byte[] oidBytes = ReadLengthPrefixedBytes();
List result = new List();
bool first = true;
int index = 0;
while (index < oidBytes.Length)
{
int subId = 0;
byte b;
do
{
b = oidBytes[index++];
if ((subId & 0xff000000) != 0)
{
throw new NotSupportedException("Oid subId > 2^31 not supported.");
}
subId = (subId << 7) | (b & 0x7f);
} while ((b & 0x80) != 0);
if (first)
{
first = false;
result.Add(subId / 40);
result.Add(subId % 40);
}
else
{
result.Add(subId);
}
}
return result.ToArray();
}
private object[] ReadSequence()
{
int length = ReadLength();
int endOffset = _index + length;
if (endOffset < 0 || endOffset > _bytes.Length)
{
throw new InvalidDataException("Invalid sequence, too long.");
}
List