1
0
mirror of https://github.com/bitwarden/server.git synced 2025-02-27 03:41:30 +01:00

license hash

This commit is contained in:
Kyle Spearrin 2017-08-16 15:16:56 -04:00
parent 92ab3db4c4
commit d9cd7880a6
4 changed files with 55 additions and 11 deletions

View File

@ -11,8 +11,11 @@ namespace Bit.Core.Models.Business
DateTime? Refresh { get; set; }
DateTime? Expires { get; set; }
bool Trial { get; set; }
string Hash { get; set; }
string Signature { get; set; }
byte[] GetSignatureData();
byte[] SignatureBytes { get; }
byte[] GetDataBytes(bool forHash = false);
byte[] ComputeHash();
bool VerifySignature(X509Certificate2 certificate);
byte[] Sign(X509Certificate2 certificate);
}

View File

@ -71,6 +71,7 @@ namespace Bit.Core.Models.Business
Trial = false;
}
Hash = Convert.ToBase64String(ComputeHash());
Signature = Convert.ToBase64String(licenseService.SignLicense(this));
}
@ -95,18 +96,29 @@ namespace Bit.Core.Models.Business
public DateTime? Refresh { get; set; }
public DateTime? Expires { get; set; }
public bool Trial { get; set; }
public string Hash { get; set; }
public string Signature { get; set; }
[JsonIgnore]
public byte[] SignatureBytes => Convert.FromBase64String(Signature);
public byte[] GetSignatureData()
public byte[] GetDataBytes(bool forHash = false)
{
string data = null;
if(Version == 1)
{
var props = typeof(OrganizationLicense)
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => !p.Name.Equals(nameof(Signature)) && !p.Name.Equals(nameof(SignatureBytes)))
.Where(p =>
!p.Name.Equals(nameof(Signature)) &&
!p.Name.Equals(nameof(SignatureBytes)) &&
(
!forHash ||
(
!p.Name.Equals(nameof(Hash)) &&
!p.Name.Equals(nameof(Issued)) &&
!p.Name.Equals(nameof(Refresh))
)
))
.OrderBy(p => p.Name)
.Select(p => $"{p.Name}:{Utilities.CoreHelpers.FormatLicenseSignatureValue(p.GetValue(this, null))}")
.Aggregate((c, n) => $"{c}|{n}");
@ -120,6 +132,14 @@ namespace Bit.Core.Models.Business
return Encoding.UTF8.GetBytes(data);
}
public byte[] ComputeHash()
{
using(var alg = SHA256.Create())
{
return alg.ComputeHash(GetDataBytes(true));
}
}
public bool CanUse(Guid installationId)
{
if(!Enabled || Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
@ -167,7 +187,7 @@ namespace Bit.Core.Models.Business
{
using(var rsa = certificate.GetRSAPublicKey())
{
return rsa.VerifyData(GetSignatureData(), SignatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
return rsa.VerifyData(GetDataBytes(), SignatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
}
@ -180,7 +200,7 @@ namespace Bit.Core.Models.Business
using(var rsa = certificate.GetRSAPrivateKey())
{
return rsa.SignData(GetSignatureData(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
return rsa.SignData(GetDataBytes(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
}
}

View File

@ -29,6 +29,8 @@ namespace Bit.Core.Models.Business
Refresh = billingInfo?.UpcomingInvoice?.Date;
Trial = (billingInfo?.Subscription?.TrialEndDate.HasValue ?? false) &&
billingInfo.Subscription.TrialEndDate.Value > DateTime.UtcNow;
Hash = Convert.ToBase64String(ComputeHash());
Signature = Convert.ToBase64String(licenseService.SignLicense(this));
}
@ -43,18 +45,29 @@ namespace Bit.Core.Models.Business
public DateTime? Refresh { get; set; }
public DateTime? Expires { get; set; }
public bool Trial { get; set; }
public string Hash { get; set; }
public string Signature { get; set; }
[JsonIgnore]
public byte[] SignatureBytes => Convert.FromBase64String(Signature);
public byte[] GetSignatureData()
public byte[] GetDataBytes(bool forHash = false)
{
string data = null;
if(Version == 1)
{
var props = typeof(UserLicense)
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => !p.Name.Equals(nameof(Signature)) && !p.Name.Equals(nameof(SignatureBytes)))
.Where(p =>
!p.Name.Equals(nameof(Signature)) &&
!p.Name.Equals(nameof(SignatureBytes)) &&
(
!forHash ||
(
!p.Name.Equals(nameof(Hash)) &&
!p.Name.Equals(nameof(Issued)) &&
!p.Name.Equals(nameof(Refresh))
)
))
.OrderBy(p => p.Name)
.Select(p => $"{p.Name}:{Utilities.CoreHelpers.FormatLicenseSignatureValue(p.GetValue(this, null))}")
.Aggregate((c, n) => $"{c}|{n}");
@ -68,6 +81,14 @@ namespace Bit.Core.Models.Business
return Encoding.UTF8.GetBytes(data);
}
public byte[] ComputeHash()
{
using(var alg = SHA256.Create())
{
return alg.ComputeHash(GetDataBytes(true));
}
}
public bool CanUse(User user)
{
if(Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
@ -109,7 +130,7 @@ namespace Bit.Core.Models.Business
{
using(var rsa = certificate.GetRSAPublicKey())
{
return rsa.VerifyData(GetSignatureData(), SignatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
return rsa.VerifyData(GetDataBytes(), SignatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
}
@ -122,7 +143,7 @@ namespace Bit.Core.Models.Business
using(var rsa = certificate.GetRSAPrivateKey())
{
return rsa.SignData(GetSignatureData(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
return rsa.SignData(GetDataBytes(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
}
}

View File

@ -152,7 +152,7 @@ namespace Bit.Core.Services
if(!organization.Seats.HasValue || organization.Seats.Value > newPlanSeats)
{
var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(organization.Id);
if(userCount >= newPlanSeats)
if(userCount > newPlanSeats)
{
throw new BadRequestException($"Your organization currently has {userCount} seats filled. Your new plan " +
$"only has ({newPlanSeats}) seats. Remove some users.");
@ -651,7 +651,7 @@ namespace Bit.Core.Services
if(license.Seats.HasValue && (!organization.Seats.HasValue || organization.Seats.Value > license.Seats.Value))
{
var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(organization.Id);
if(userCount >= license.Seats.Value)
if(userCount > license.Seats.Value)
{
throw new BadRequestException($"Your organization currently has {userCount} seats filled. " +
$"Your new license only has ({ license.Seats.Value}) seats. Remove some users.");