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? Refresh { get; set; }
DateTime? Expires { get; set; } DateTime? Expires { get; set; }
bool Trial { get; set; } bool Trial { get; set; }
string Hash { get; set; }
string Signature { get; set; } string Signature { get; set; }
byte[] GetSignatureData(); byte[] SignatureBytes { get; }
byte[] GetDataBytes(bool forHash = false);
byte[] ComputeHash();
bool VerifySignature(X509Certificate2 certificate); bool VerifySignature(X509Certificate2 certificate);
byte[] Sign(X509Certificate2 certificate); byte[] Sign(X509Certificate2 certificate);
} }

View File

@ -71,6 +71,7 @@ namespace Bit.Core.Models.Business
Trial = false; Trial = false;
} }
Hash = Convert.ToBase64String(ComputeHash());
Signature = Convert.ToBase64String(licenseService.SignLicense(this)); Signature = Convert.ToBase64String(licenseService.SignLicense(this));
} }
@ -95,18 +96,29 @@ namespace Bit.Core.Models.Business
public DateTime? Refresh { get; set; } public DateTime? Refresh { get; set; }
public DateTime? Expires { get; set; } public DateTime? Expires { get; set; }
public bool Trial { get; set; } public bool Trial { get; set; }
public string Hash { get; set; }
public string Signature { get; set; } public string Signature { get; set; }
[JsonIgnore] [JsonIgnore]
public byte[] SignatureBytes => Convert.FromBase64String(Signature); public byte[] SignatureBytes => Convert.FromBase64String(Signature);
public byte[] GetSignatureData() public byte[] GetDataBytes(bool forHash = false)
{ {
string data = null; string data = null;
if(Version == 1) if(Version == 1)
{ {
var props = typeof(OrganizationLicense) var props = typeof(OrganizationLicense)
.GetProperties(BindingFlags.Public | BindingFlags.Instance) .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) .OrderBy(p => p.Name)
.Select(p => $"{p.Name}:{Utilities.CoreHelpers.FormatLicenseSignatureValue(p.GetValue(this, null))}") .Select(p => $"{p.Name}:{Utilities.CoreHelpers.FormatLicenseSignatureValue(p.GetValue(this, null))}")
.Aggregate((c, n) => $"{c}|{n}"); .Aggregate((c, n) => $"{c}|{n}");
@ -120,6 +132,14 @@ namespace Bit.Core.Models.Business
return Encoding.UTF8.GetBytes(data); return Encoding.UTF8.GetBytes(data);
} }
public byte[] ComputeHash()
{
using(var alg = SHA256.Create())
{
return alg.ComputeHash(GetDataBytes(true));
}
}
public bool CanUse(Guid installationId) public bool CanUse(Guid installationId)
{ {
if(!Enabled || Issued > DateTime.UtcNow || Expires < DateTime.UtcNow) if(!Enabled || Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
@ -167,7 +187,7 @@ namespace Bit.Core.Models.Business
{ {
using(var rsa = certificate.GetRSAPublicKey()) 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()) 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; Refresh = billingInfo?.UpcomingInvoice?.Date;
Trial = (billingInfo?.Subscription?.TrialEndDate.HasValue ?? false) && Trial = (billingInfo?.Subscription?.TrialEndDate.HasValue ?? false) &&
billingInfo.Subscription.TrialEndDate.Value > DateTime.UtcNow; billingInfo.Subscription.TrialEndDate.Value > DateTime.UtcNow;
Hash = Convert.ToBase64String(ComputeHash());
Signature = Convert.ToBase64String(licenseService.SignLicense(this)); Signature = Convert.ToBase64String(licenseService.SignLicense(this));
} }
@ -43,18 +45,29 @@ namespace Bit.Core.Models.Business
public DateTime? Refresh { get; set; } public DateTime? Refresh { get; set; }
public DateTime? Expires { get; set; } public DateTime? Expires { get; set; }
public bool Trial { get; set; } public bool Trial { get; set; }
public string Hash { get; set; }
public string Signature { get; set; } public string Signature { get; set; }
[JsonIgnore] [JsonIgnore]
public byte[] SignatureBytes => Convert.FromBase64String(Signature); public byte[] SignatureBytes => Convert.FromBase64String(Signature);
public byte[] GetSignatureData() public byte[] GetDataBytes(bool forHash = false)
{ {
string data = null; string data = null;
if(Version == 1) if(Version == 1)
{ {
var props = typeof(UserLicense) var props = typeof(UserLicense)
.GetProperties(BindingFlags.Public | BindingFlags.Instance) .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) .OrderBy(p => p.Name)
.Select(p => $"{p.Name}:{Utilities.CoreHelpers.FormatLicenseSignatureValue(p.GetValue(this, null))}") .Select(p => $"{p.Name}:{Utilities.CoreHelpers.FormatLicenseSignatureValue(p.GetValue(this, null))}")
.Aggregate((c, n) => $"{c}|{n}"); .Aggregate((c, n) => $"{c}|{n}");
@ -68,6 +81,14 @@ namespace Bit.Core.Models.Business
return Encoding.UTF8.GetBytes(data); return Encoding.UTF8.GetBytes(data);
} }
public byte[] ComputeHash()
{
using(var alg = SHA256.Create())
{
return alg.ComputeHash(GetDataBytes(true));
}
}
public bool CanUse(User user) public bool CanUse(User user)
{ {
if(Issued > DateTime.UtcNow || Expires < DateTime.UtcNow) if(Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
@ -109,7 +130,7 @@ namespace Bit.Core.Models.Business
{ {
using(var rsa = certificate.GetRSAPublicKey()) 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()) 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) if(!organization.Seats.HasValue || organization.Seats.Value > newPlanSeats)
{ {
var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(organization.Id); 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 " + throw new BadRequestException($"Your organization currently has {userCount} seats filled. Your new plan " +
$"only has ({newPlanSeats}) seats. Remove some users."); $"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)) if(license.Seats.HasValue && (!organization.Seats.HasValue || organization.Seats.Value > license.Seats.Value))
{ {
var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(organization.Id); 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. " + throw new BadRequestException($"Your organization currently has {userCount} seats filled. " +
$"Your new license only has ({ license.Seats.Value}) seats. Remove some users."); $"Your new license only has ({ license.Seats.Value}) seats. Remove some users.");