From 83c84a7cc012b803a497999c7eb353462b3e8120 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 5 Jun 2025 12:43:25 +0200 Subject: [PATCH] Add initial get keys endpoint --- src/Api/Controllers/UsersController.cs | 33 ------------ .../Controllers/UsersController.cs | 54 +++++++++++++++++++ .../Response/PrivateKeysResponseModel.cs | 30 +++++++++++ .../Response/PublicKeysResponseModel.cs | 23 ++++++++ .../Models/Response/UserKeyResponseModel.cs | 16 ++++++ .../Data/PublicKeyEncryptionKeyPairData.cs | 9 ++++ .../Models/Data/UserAccountKeysData.cs | 7 +++ 7 files changed, 139 insertions(+), 33 deletions(-) delete mode 100644 src/Api/Controllers/UsersController.cs create mode 100644 src/Api/KeyManagement/Controllers/UsersController.cs create mode 100644 src/Api/KeyManagement/Models/Response/PrivateKeysResponseModel.cs create mode 100644 src/Api/KeyManagement/Models/Response/PublicKeysResponseModel.cs create mode 100644 src/Api/KeyManagement/Models/Response/UserKeyResponseModel.cs create mode 100644 src/Core/KeyManagement/Models/Data/PublicKeyEncryptionKeyPairData.cs create mode 100644 src/Core/KeyManagement/Models/Data/UserAccountKeysData.cs diff --git a/src/Api/Controllers/UsersController.cs b/src/Api/Controllers/UsersController.cs deleted file mode 100644 index 4dfd047d37..0000000000 --- a/src/Api/Controllers/UsersController.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Bit.Api.Models.Response; -using Bit.Core.Exceptions; -using Bit.Core.Repositories; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace Bit.Api.Controllers; - -[Route("users")] -[Authorize("Application")] -public class UsersController : Controller -{ - private readonly IUserRepository _userRepository; - - public UsersController( - IUserRepository userRepository) - { - _userRepository = userRepository; - } - - [HttpGet("{id}/public-key")] - public async Task Get(string id) - { - var guidId = new Guid(id); - var key = await _userRepository.GetPublicKeyAsync(guidId); - if (key == null) - { - throw new NotFoundException(); - } - - return new UserKeyResponseModel(guidId, key); - } -} diff --git a/src/Api/KeyManagement/Controllers/UsersController.cs b/src/Api/KeyManagement/Controllers/UsersController.cs new file mode 100644 index 0000000000..20405476ac --- /dev/null +++ b/src/Api/KeyManagement/Controllers/UsersController.cs @@ -0,0 +1,54 @@ +using Bit.Api.KeyManagement.Models.Response; +using Bit.Core.Exceptions; +using Bit.Core.KeyManagement.Repositories; +using Bit.Core.Repositories; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using UserKeyResponseModel = Bit.Api.Models.Response.UserKeyResponseModel; + +namespace Bit.Api.Controllers; + +[Route("users")] +[Authorize("Application")] +public class UsersController : Controller +{ + private readonly IUserRepository _userRepository; + private readonly IUserSignatureKeyPairRepository _signatureKeyPairRepository; + + public UsersController( + IUserRepository userRepository, + IUserSignatureKeyPairRepository signatureKeyPairRepository) + { + _userRepository = userRepository; + _signatureKeyPairRepository = signatureKeyPairRepository; + } + + [HttpGet("{id}/public-key")] + public async Task Get(string id) + { + var guidId = new Guid(id); + var key = await _userRepository.GetPublicKeyAsync(guidId); + if (key == null) + { + throw new NotFoundException(); + } + + return new UserKeyResponseModel(guidId, key); + } + + [HttpGet("{id}/keys")] + public async Task GetAccountKeys(string id) + { + var guidId = new Guid(id); + var user = await _userRepository.GetByIdAsync(guidId); + if (user == null) + { + throw new NotFoundException(); + } + + var signingKeys = await _signatureKeyPairRepository.GetByUserIdAsync(guidId); + var verifyingKey = signingKeys?.VerifyingKey; + + return new PublicKeysResponseModel(verifyingKey, user.PublicKey, null); + } +} diff --git a/src/Api/KeyManagement/Models/Response/PrivateKeysResponseModel.cs b/src/Api/KeyManagement/Models/Response/PrivateKeysResponseModel.cs new file mode 100644 index 0000000000..58555ea50f --- /dev/null +++ b/src/Api/KeyManagement/Models/Response/PrivateKeysResponseModel.cs @@ -0,0 +1,30 @@ +using Bit.Core.KeyManagement.Models.Data; +using Bit.Core.Models.Api; + +namespace Bit.Api.Models.Response; + +/// +/// This response model is used to return keys of a user - downstream of the user key - to the client. +/// This includes the private keys (signature/encryption), and proof tying one to another. This could +/// also be used to contain further user-owned keys in the future (per-vault keys, etc). This should +/// not be used to contain keys not just owned by the user (e.g. organization keys). +/// +public class PrivateAccountKeysResponseModel : ResponseModel +{ + public PrivateAccountKeysResponseModel(UserAccountKeysData accountKeys) : base("accountKeys") + { + if (accountKeys != null) + { + SignatureKeyPair = accountKeys.signatureKeyPairData; + } + PublicKeyEncryptionKeyPair = accountKeys.PublicKeyEncryptionKeyPairData; + } + + public PrivateAccountKeysResponseModel() : base("accountKeys") + { + } + + public SignatureKeyPairData SignatureKeyPair { get; set; } + public PublicKeyEncryptionKeyPairData PublicKeyEncryptionKeyPair { get; set; } + +} diff --git a/src/Api/KeyManagement/Models/Response/PublicKeysResponseModel.cs b/src/Api/KeyManagement/Models/Response/PublicKeysResponseModel.cs new file mode 100644 index 0000000000..c2f45de977 --- /dev/null +++ b/src/Api/KeyManagement/Models/Response/PublicKeysResponseModel.cs @@ -0,0 +1,23 @@ +using Bit.Core.Models.Api; + +namespace Bit.Api.KeyManagement.Models.Response; + +/// +/// This response model is used to return the public keys of a user, to any other registered user or entity on the server. +/// It can contain public keys (signature/encryption), and proofs between the two. It does not contain (encrypted) private keys. +/// +public class PublicKeysResponseModel : ResponseModel +{ + public PublicKeysResponseModel(string verifyingKey, string publicKey, string signedPublicKey) + : base("publicKeys") + { + VerifyingKey = verifyingKey; + SignedPublicKey = signedPublicKey; + PublicKey = publicKey; + } + + public string VerifyingKey { get; set; } + public string SignedPublicKey { get; set; } + [System.Obsolete("Use SignedPublicKey for new code.")] + public string PublicKey { get; set; } +} diff --git a/src/Api/KeyManagement/Models/Response/UserKeyResponseModel.cs b/src/Api/KeyManagement/Models/Response/UserKeyResponseModel.cs new file mode 100644 index 0000000000..95bcb82bd7 --- /dev/null +++ b/src/Api/KeyManagement/Models/Response/UserKeyResponseModel.cs @@ -0,0 +1,16 @@ +using Bit.Core.Models.Api; + +namespace Bit.Api.KeyManagement.Models.Response; + +public class UserKeyResponseModel : ResponseModel +{ + public UserKeyResponseModel(Guid id, string key) + : base("userKey") + { + UserId = id; + PublicKey = key; + } + + public Guid UserId { get; set; } + public string PublicKey { get; set; } +} diff --git a/src/Core/KeyManagement/Models/Data/PublicKeyEncryptionKeyPairData.cs b/src/Core/KeyManagement/Models/Data/PublicKeyEncryptionKeyPairData.cs new file mode 100644 index 0000000000..ed39f1dfc7 --- /dev/null +++ b/src/Core/KeyManagement/Models/Data/PublicKeyEncryptionKeyPairData.cs @@ -0,0 +1,9 @@ +namespace Bit.Core.KeyManagement.Models.Data; + +public class PublicKeyEncryptionKeyPairData +{ + public string WrappedPrivateKey { get; set; } + public string SignedPublicKey { get; set; } + [System.Obsolete("Use SignedPublicKey instead for new code.")] + public string PublicKey { get; set; } +} diff --git a/src/Core/KeyManagement/Models/Data/UserAccountKeysData.cs b/src/Core/KeyManagement/Models/Data/UserAccountKeysData.cs new file mode 100644 index 0000000000..95fa221510 --- /dev/null +++ b/src/Core/KeyManagement/Models/Data/UserAccountKeysData.cs @@ -0,0 +1,7 @@ +namespace Bit.Core.KeyManagement.Models.Data; + +public class UserAccountKeysData +{ + public PublicKeyEncryptionKeyPairData PublicKeyEncryptionKeyPairData { get; set; } + public SignatureKeyPairData signatureKeyPairData { get; set; } +}